From 40e635b704cafd7c0fb68e58257462f01c6522b8 Mon Sep 17 00:00:00 2001 From: Manuel Date: Fri, 5 Dec 2025 15:50:28 +0100 Subject: [PATCH 01/30] feat(db): Add basic structure for db view #50 --- .../db/migration/V0_0_14__add_member_view.sql | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) create mode 100644 backend/src/main/resources/db/migration/V0_0_14__add_member_view.sql diff --git a/backend/src/main/resources/db/migration/V0_0_14__add_member_view.sql b/backend/src/main/resources/db/migration/V0_0_14__add_member_view.sql new file mode 100644 index 000000000..523bfe035 --- /dev/null +++ b/backend/src/main/resources/db/migration/V0_0_14__add_member_view.sql @@ -0,0 +1,18 @@ +CREATE VIEW member_full_view AS +SELECT + m.id, + m.first_name, + m.last_name, + m.abbreviation, + m.employment_state, + m.date_of_hire, + m.birth_date, + m.organisation_unit + +-- c. + +FROM member m + LEFT JOIN certificate c ON c.member_id = m.id + LEFT JOIN degree d ON d.member_id = m.id + LEFT JOIN experience e ON e.member_id = m.id + LEFT JOIN calculation cal ON cal.member_id = m.id; \ No newline at end of file From 7c98a2d7f79ed9f834c0b8c353d4d41274f4b168 Mon Sep 17 00:00:00 2001 From: Manuel Date: Mon, 8 Dec 2025 15:51:52 +0100 Subject: [PATCH 02/30] feat(backend): Add endpoint for the overview #50 --- .../main/java/ch/puzzle/pcts/Constants.java | 1 + .../controller/MemberOverviewController.java | 41 ++++++ .../pcts/dto/memberoverview/MemberCvDto.java | 11 ++ .../dto/memberoverview/MemberOverviewDto.java | 6 + .../pcts/mapper/MemberOverviewMapper.java | 24 ++++ .../model/memberoverview/MemberOverview.java | 32 +++++ .../repository/MemberOverviewRepository.java | 11 ++ .../MemberOverviewBusinessService.java | 39 ++++++ .../MemberOverviewPersistenceService.java | 20 +++ .../db/migration/V0_0_14__add_member_view.sql | 129 +++++++++++++++--- 10 files changed, 297 insertions(+), 17 deletions(-) create mode 100644 backend/src/main/java/ch/puzzle/pcts/controller/MemberOverviewController.java create mode 100644 backend/src/main/java/ch/puzzle/pcts/dto/memberoverview/MemberCvDto.java create mode 100644 backend/src/main/java/ch/puzzle/pcts/dto/memberoverview/MemberOverviewDto.java create mode 100644 backend/src/main/java/ch/puzzle/pcts/mapper/MemberOverviewMapper.java create mode 100644 backend/src/main/java/ch/puzzle/pcts/model/memberoverview/MemberOverview.java create mode 100644 backend/src/main/java/ch/puzzle/pcts/repository/MemberOverviewRepository.java create mode 100644 backend/src/main/java/ch/puzzle/pcts/service/business/MemberOverviewBusinessService.java create mode 100644 backend/src/main/java/ch/puzzle/pcts/service/persistence/MemberOverviewPersistenceService.java diff --git a/backend/src/main/java/ch/puzzle/pcts/Constants.java b/backend/src/main/java/ch/puzzle/pcts/Constants.java index c5b59fd90..d34d2ea6d 100644 --- a/backend/src/main/java/ch/puzzle/pcts/Constants.java +++ b/backend/src/main/java/ch/puzzle/pcts/Constants.java @@ -14,6 +14,7 @@ public class Constants { public static final String LEADERSHIP_EXPERIENCE_TYPE = "leadershipExperienceType"; public static final String CALCULATION = "calculation"; public static final String LEADERSHIP_EXPERIENCE = "leadershipExperience"; + public static final String MEMBER_OVERVIEW = "memberOverview"; private Constants() { } diff --git a/backend/src/main/java/ch/puzzle/pcts/controller/MemberOverviewController.java b/backend/src/main/java/ch/puzzle/pcts/controller/MemberOverviewController.java new file mode 100644 index 000000000..0b0cec602 --- /dev/null +++ b/backend/src/main/java/ch/puzzle/pcts/controller/MemberOverviewController.java @@ -0,0 +1,41 @@ +package ch.puzzle.pcts.controller; + +import ch.puzzle.pcts.dto.member.MemberDto; +import ch.puzzle.pcts.dto.memberoverview.MemberOverviewDto; +import ch.puzzle.pcts.mapper.MemberOverviewMapper; +import ch.puzzle.pcts.model.memberoverview.MemberOverview; +import ch.puzzle.pcts.service.business.MemberOverviewBusinessService; +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.tags.Tag; +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.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +@RestController +@RequestMapping("/api/v1/member-overviews") +@Tag(name = "member-overviews", description = "Get the member and everything associated with him") +public class MemberOverviewController { + private final MemberOverviewMapper mapper; + private final MemberOverviewBusinessService service; + + public MemberOverviewController(MemberOverviewMapper mapper, MemberOverviewBusinessService service) { + this.mapper = mapper; + this.service = service; + } + + @Operation(summary = "Get the member-overview by ID") + @ApiResponse(responseCode = "200", description = "Successfully retrieved the member-overview.", content = { + @Content(mediaType = "application/json", schema = @Schema(implementation = MemberDto.class)) }) + @GetMapping("{memberOverviewId}") + public ResponseEntity getMemberOverviewById(@Parameter(description = "ID of the member-overview to retrieve.", required = true) + @PathVariable Long memberOverviewId) { + MemberOverview memberOverview = service.getById(memberOverviewId); + return ResponseEntity.ok(mapper.toDto(memberOverview)); + } +} diff --git a/backend/src/main/java/ch/puzzle/pcts/dto/memberoverview/MemberCvDto.java b/backend/src/main/java/ch/puzzle/pcts/dto/memberoverview/MemberCvDto.java new file mode 100644 index 000000000..3713605e6 --- /dev/null +++ b/backend/src/main/java/ch/puzzle/pcts/dto/memberoverview/MemberCvDto.java @@ -0,0 +1,11 @@ +package ch.puzzle.pcts.dto.memberoverview; + +import ch.puzzle.pcts.dto.certificate.CertificateDto; +import ch.puzzle.pcts.dto.degree.DegreeDto; +import ch.puzzle.pcts.dto.experience.ExperienceDto; +import ch.puzzle.pcts.dto.leadershipexperience.LeadershipExperienceDto; +import java.util.List; + +public record MemberCvDto(List degrees, List experiences, List certificates, + List leadershipExperiences) { +} diff --git a/backend/src/main/java/ch/puzzle/pcts/dto/memberoverview/MemberOverviewDto.java b/backend/src/main/java/ch/puzzle/pcts/dto/memberoverview/MemberOverviewDto.java new file mode 100644 index 000000000..18aca4c27 --- /dev/null +++ b/backend/src/main/java/ch/puzzle/pcts/dto/memberoverview/MemberOverviewDto.java @@ -0,0 +1,6 @@ +package ch.puzzle.pcts.dto.memberoverview; + +import ch.puzzle.pcts.dto.member.MemberDto; + +public record MemberOverviewDto(MemberDto member, MemberCvDto cv) { +} diff --git a/backend/src/main/java/ch/puzzle/pcts/mapper/MemberOverviewMapper.java b/backend/src/main/java/ch/puzzle/pcts/mapper/MemberOverviewMapper.java new file mode 100644 index 000000000..b6ad76756 --- /dev/null +++ b/backend/src/main/java/ch/puzzle/pcts/mapper/MemberOverviewMapper.java @@ -0,0 +1,24 @@ +package ch.puzzle.pcts.mapper; + +import ch.puzzle.pcts.dto.memberoverview.MemberOverviewDto; +import ch.puzzle.pcts.model.memberoverview.MemberOverview; +import com.fasterxml.jackson.databind.ObjectMapper; +import org.springframework.stereotype.Component; + +@Component +public class MemberOverviewMapper { + + private final ObjectMapper mapper; + + public MemberOverviewMapper(ObjectMapper mapper) { + this.mapper = mapper; + } + + public MemberOverviewDto toDto(MemberOverview entity) { + try { + return mapper.readValue(entity.getOverview(), MemberOverviewDto.class); + } catch (Exception e) { + throw new IllegalStateException("Failed to map overview JSON", e); + } + } +} diff --git a/backend/src/main/java/ch/puzzle/pcts/model/memberoverview/MemberOverview.java b/backend/src/main/java/ch/puzzle/pcts/model/memberoverview/MemberOverview.java new file mode 100644 index 000000000..6cda575b7 --- /dev/null +++ b/backend/src/main/java/ch/puzzle/pcts/model/memberoverview/MemberOverview.java @@ -0,0 +1,32 @@ +package ch.puzzle.pcts.model.memberoverview; + +import ch.puzzle.pcts.model.Model; +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.Id; +import jakarta.persistence.Table; + +@Entity +@Table(name = "member_overview_view") +public class MemberOverview implements Model { + + @Id + @Column(name = "member_id") + private Long id; + + @Column(columnDefinition = "jsonb") + private String overview; + + public Long getId() { + return id; + } + + @Override + public void setId(Long id) { + this.id = id; + } + + public String getOverview() { + return overview; + } +} diff --git a/backend/src/main/java/ch/puzzle/pcts/repository/MemberOverviewRepository.java b/backend/src/main/java/ch/puzzle/pcts/repository/MemberOverviewRepository.java new file mode 100644 index 000000000..c6ae67bdb --- /dev/null +++ b/backend/src/main/java/ch/puzzle/pcts/repository/MemberOverviewRepository.java @@ -0,0 +1,11 @@ +package ch.puzzle.pcts.repository; + +import ch.puzzle.pcts.model.memberoverview.MemberOverview; +import java.util.Optional; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; + +@Repository +public interface MemberOverviewRepository extends JpaRepository { + Optional findOverviewById(Long id); +} diff --git a/backend/src/main/java/ch/puzzle/pcts/service/business/MemberOverviewBusinessService.java b/backend/src/main/java/ch/puzzle/pcts/service/business/MemberOverviewBusinessService.java new file mode 100644 index 000000000..7bfac8e16 --- /dev/null +++ b/backend/src/main/java/ch/puzzle/pcts/service/business/MemberOverviewBusinessService.java @@ -0,0 +1,39 @@ +package ch.puzzle.pcts.service.business; + +import static ch.puzzle.pcts.Constants.MEMBER_OVERVIEW; + +import ch.puzzle.pcts.dto.error.ErrorKey; +import ch.puzzle.pcts.dto.error.FieldKey; +import ch.puzzle.pcts.dto.error.GenericErrorDto; +import ch.puzzle.pcts.exception.PCTSException; +import ch.puzzle.pcts.model.memberoverview.MemberOverview; +import ch.puzzle.pcts.service.persistence.MemberOverviewPersistenceService; +import java.util.List; +import java.util.Map; +import org.springframework.http.HttpStatus; +import org.springframework.stereotype.Service; + +@Service +public class MemberOverviewBusinessService { + + private final MemberOverviewPersistenceService persistenceService; + + public MemberOverviewBusinessService(MemberOverviewPersistenceService persistenceService) { + this.persistenceService = persistenceService; + } + + public MemberOverview getById(Long id) { + return persistenceService.getById(id).orElseThrow(() -> { + Map attributes = Map + .of(FieldKey.ENTITY, entityName(), FieldKey.FIELD, "id", FieldKey.IS, id.toString()); + + GenericErrorDto error = new GenericErrorDto(ErrorKey.NOT_FOUND, attributes); + + return new PCTSException(HttpStatus.NOT_FOUND, List.of(error)); + }); + } + + protected String entityName() { + return MEMBER_OVERVIEW; + } +} diff --git a/backend/src/main/java/ch/puzzle/pcts/service/persistence/MemberOverviewPersistenceService.java b/backend/src/main/java/ch/puzzle/pcts/service/persistence/MemberOverviewPersistenceService.java new file mode 100644 index 000000000..12033d051 --- /dev/null +++ b/backend/src/main/java/ch/puzzle/pcts/service/persistence/MemberOverviewPersistenceService.java @@ -0,0 +1,20 @@ +package ch.puzzle.pcts.service.persistence; + +import ch.puzzle.pcts.model.memberoverview.MemberOverview; +import ch.puzzle.pcts.repository.MemberOverviewRepository; +import java.util.Optional; +import org.springframework.stereotype.Service; + +@Service +public class MemberOverviewPersistenceService { + + MemberOverviewRepository repository; + + public MemberOverviewPersistenceService(MemberOverviewRepository repository) { + this.repository = repository; + } + + public Optional getById(Long id) { + return repository.findOverviewById(id); + } +} diff --git a/backend/src/main/resources/db/migration/V0_0_14__add_member_view.sql b/backend/src/main/resources/db/migration/V0_0_14__add_member_view.sql index 523bfe035..cfe33ba4b 100644 --- a/backend/src/main/resources/db/migration/V0_0_14__add_member_view.sql +++ b/backend/src/main/resources/db/migration/V0_0_14__add_member_view.sql @@ -1,18 +1,113 @@ -CREATE VIEW member_full_view AS -SELECT - m.id, - m.first_name, - m.last_name, - m.abbreviation, - m.employment_state, - m.date_of_hire, - m.birth_date, - m.organisation_unit - --- c. +CREATE OR REPLACE FUNCTION build_member_overview(p_member_id BIGINT) + RETURNS JSONB + LANGUAGE sql + STABLE +AS $$ +WITH base AS ( + SELECT * + FROM member m + WHERE m.id = p_member_id +), + cert AS ( + SELECT jsonb_agg( + jsonb_build_object( + 'id', c.id, + 'certificate', jsonb_build_object( + 'id', ct.id, + 'name', ct.name, + 'points', ct.points, + 'comment', ct.comment + ), + 'completion', c.completed_at, + 'validUntil', c.valid_until, + 'comment', c.comment + ) + ORDER BY c.completed_at + ) AS certificates + FROM certificate c + LEFT JOIN certificate_type ct + ON ct.id = c.certificate_type_id + WHERE c.member_id = p_member_id + AND c.deleted_at IS NULL + ), + degrees AS ( + SELECT jsonb_agg( + jsonb_build_object( + 'id', d.id, + 'name', d.name, + 'startDate', d.start_date, + 'endDate', d.end_date, + 'institution', d.institution, + 'completed', d.completed, + 'type', jsonb_build_object( + 'degree_type_id', dt.id, + 'name', dt.name, + 'highlyRelevantPoints', dt.highly_relevant_points, + 'limitedRelevantPoints', dt.limited_relevant_points, + 'littleRelevantPoints', dt.little_relevant_points + ) + ) + ORDER BY d.start_date + ) AS degrees + FROM degree d + LEFT JOIN degree_type dt ON dt.id = d.degree_type_id + WHERE d.member_id = p_member_id + ), + experience AS ( + SELECT jsonb_agg( + jsonb_build_object( + 'id', e.id, + 'name', e.name, + 'employer', e.employer, + 'startDate', e.start_date, + 'endDate', e.end_date, + 'percent', e.percent, + 'comment', e.comment, + 'type', jsonb_build_object( + 'id', et.id, + 'name', et.name, + 'highlyRelevantPoints', et.highly_relevant_points, + 'limitedRelevantPoints', et.limited_relevant_points, + 'littleRelevantPoints', et.little_relevant_points + ) + ) + ORDER BY e.start_date + ) AS experiences + FROM experience e + LEFT JOIN experience_type et ON et.id = e.experience_type_id + WHERE e.member_id = p_member_id + ) +SELECT jsonb_build_object( + 'member', jsonb_build_object( + 'id', m.id, + 'firstName', m.first_name, + 'lastName', m.last_name, + 'abbreviation', m.abbreviation, + 'dateOfBirth', m.birth_date, + 'dateOfHire', m.date_of_hire, + 'employmentState', m.employment_state, + 'organisationUnit', jsonb_build_object( + 'id', ou.id, + 'name', ou.name + ) + ), + 'cv', jsonb_build_object( + 'degrees', COALESCE(d.degrees, '[]'::jsonb), + 'certificates', COALESCE(c.certificates, '[]'::jsonb), + 'experiences', COALESCE(x.experiences, '[]'::jsonb), + 'leadershipExperiences', '[]'::jsonb -- You will fill these in + ), + 'calculations', '[]'::jsonb -- You will fill this in later + ) +FROM base m + LEFT JOIN organisation_unit ou ON ou.id = m.organisation_unit + LEFT JOIN cert c ON TRUE + LEFT JOIN degrees d ON TRUE + LEFT JOIN experience x ON TRUE; +$$; -FROM member m - LEFT JOIN certificate c ON c.member_id = m.id - LEFT JOIN degree d ON d.member_id = m.id - LEFT JOIN experience e ON e.member_id = m.id - LEFT JOIN calculation cal ON cal.member_id = m.id; \ No newline at end of file +CREATE OR REPLACE VIEW member_overview_view AS +SELECT + m.id AS member_id, + build_member_overview(m.id) AS overview +FROM member m; From 5226b4cab68a5bcfff814992860d8671388f89cd Mon Sep 17 00:00:00 2001 From: Manuel Date: Tue, 9 Dec 2025 13:21:18 +0100 Subject: [PATCH 03/30] feat(backend): Add leadershipExperiences and calculation endpoint to overview as well #50 --- .../controller/MemberOverviewController.java | 2 +- .../dto/memberoverview/MemberOverviewDto.java | 4 +- .../db/migration/V0_0_14__add_member_view.sql | 60 +++++++++++++++++-- 3 files changed, 58 insertions(+), 8 deletions(-) diff --git a/backend/src/main/java/ch/puzzle/pcts/controller/MemberOverviewController.java b/backend/src/main/java/ch/puzzle/pcts/controller/MemberOverviewController.java index 0b0cec602..f6d086192 100644 --- a/backend/src/main/java/ch/puzzle/pcts/controller/MemberOverviewController.java +++ b/backend/src/main/java/ch/puzzle/pcts/controller/MemberOverviewController.java @@ -19,7 +19,7 @@ @RestController @RequestMapping("/api/v1/member-overviews") -@Tag(name = "member-overviews", description = "Get the member and everything associated with him") +@Tag(name = "member-overviews", description = "Get the member and everything associated with the member") public class MemberOverviewController { private final MemberOverviewMapper mapper; private final MemberOverviewBusinessService service; diff --git a/backend/src/main/java/ch/puzzle/pcts/dto/memberoverview/MemberOverviewDto.java b/backend/src/main/java/ch/puzzle/pcts/dto/memberoverview/MemberOverviewDto.java index 18aca4c27..243287785 100644 --- a/backend/src/main/java/ch/puzzle/pcts/dto/memberoverview/MemberOverviewDto.java +++ b/backend/src/main/java/ch/puzzle/pcts/dto/memberoverview/MemberOverviewDto.java @@ -1,6 +1,8 @@ package ch.puzzle.pcts.dto.memberoverview; +import ch.puzzle.pcts.dto.calculation.CalculationDto; import ch.puzzle.pcts.dto.member.MemberDto; +import java.util.List; -public record MemberOverviewDto(MemberDto member, MemberCvDto cv) { +public record MemberOverviewDto(MemberDto member, MemberCvDto cv, List calculations) { } diff --git a/backend/src/main/resources/db/migration/V0_0_14__add_member_view.sql b/backend/src/main/resources/db/migration/V0_0_14__add_member_view.sql index cfe33ba4b..73183a302 100644 --- a/backend/src/main/resources/db/migration/V0_0_14__add_member_view.sql +++ b/backend/src/main/resources/db/migration/V0_0_14__add_member_view.sql @@ -8,6 +8,7 @@ WITH base AS ( FROM member m WHERE m.id = p_member_id ), + cert AS ( SELECT jsonb_agg( jsonb_build_object( @@ -18,18 +19,41 @@ WITH base AS ( 'points', ct.points, 'comment', ct.comment ), - 'completion', c.completed_at, + 'completedAt', c.completed_at, 'validUntil', c.valid_until, 'comment', c.comment ) ORDER BY c.completed_at ) AS certificates FROM certificate c - LEFT JOIN certificate_type ct - ON ct.id = c.certificate_type_id + LEFT JOIN certificate_type ct ON ct.id = c.certificate_type_id + WHERE c.member_id = p_member_id + AND c.deleted_at IS NULL + AND ct.certificate_kind = 'CERTIFICATE' + ), + + leader AS ( + SELECT jsonb_agg( + jsonb_build_object( + 'id', c.id, + 'comment', c.comment, + 'experience', jsonb_build_object( + 'id', ct.id, + 'name', ct.name, + 'experienceKind', ct.certificate_kind, + 'points', ct.points, + 'comment', ct.comment + ) + ) + ORDER BY c.completed_at + ) AS leadership_experiences + FROM certificate c + LEFT JOIN certificate_type ct ON ct.id = c.certificate_type_id WHERE c.member_id = p_member_id AND c.deleted_at IS NULL + AND ct.certificate_kind != 'CERTIFICATE' ), + degrees AS ( SELECT jsonb_agg( jsonb_build_object( @@ -53,6 +77,7 @@ WITH base AS ( LEFT JOIN degree_type dt ON dt.id = d.degree_type_id WHERE d.member_id = p_member_id ), + experience AS ( SELECT jsonb_agg( jsonb_build_object( @@ -76,7 +101,28 @@ WITH base AS ( FROM experience e LEFT JOIN experience_type et ON et.id = e.experience_type_id WHERE e.member_id = p_member_id + ), + + calcs AS ( + SELECT jsonb_agg( + jsonb_build_object( + 'id', c.id, + 'state', c.state, + 'publicationDate', c.publication_date, + 'publicizedBy', c.publicized_by, + 'role', jsonb_build_object( + 'id', r.id, + 'name', r.name, + 'isManagement', r.is_management + ) + ) + ORDER BY c.id + ) AS calculations + FROM calculation c + LEFT JOIN role r ON r.id = c.role_id + WHERE c.member_id = p_member_id ) + SELECT jsonb_build_object( 'member', jsonb_build_object( 'id', m.id, @@ -95,15 +141,17 @@ SELECT jsonb_build_object( 'degrees', COALESCE(d.degrees, '[]'::jsonb), 'certificates', COALESCE(c.certificates, '[]'::jsonb), 'experiences', COALESCE(x.experiences, '[]'::jsonb), - 'leadershipExperiences', '[]'::jsonb -- You will fill these in + 'leadershipExperiences', COALESCE(l.leadership_experiences, '[]'::jsonb) ), - 'calculations', '[]'::jsonb -- You will fill this in later + 'calculations', COALESCE(ca.calculations, '[]'::jsonb) ) FROM base m LEFT JOIN organisation_unit ou ON ou.id = m.organisation_unit LEFT JOIN cert c ON TRUE + LEFT JOIN leader l ON TRUE LEFT JOIN degrees d ON TRUE - LEFT JOIN experience x ON TRUE; + LEFT JOIN experience x ON TRUE + LEFT JOIN calcs ca ON TRUE; $$; CREATE OR REPLACE VIEW member_overview_view AS From 743c1278c407802a8bba8a096507ce4dde204b40 Mon Sep 17 00:00:00 2001 From: Manuel Date: Wed, 10 Dec 2025 15:54:46 +0100 Subject: [PATCH 04/30] refactor(backend): Utilize objects instead of pure json when creating the view #50 --- .../controller/MemberOverviewController.java | 5 +- .../pcts/mapper/MemberOverviewMapper.java | 160 ++- .../model/memberoverview/MemberOverview.java | 999 +++++++++++++++++- .../repository/MemberOverviewRepository.java | 4 +- .../MemberOverviewBusinessService.java | 17 +- .../MemberOverviewPersistenceService.java | 6 +- .../db/migration/V0_0_14__add_member_view.sql | 342 +++--- 7 files changed, 1329 insertions(+), 204 deletions(-) diff --git a/backend/src/main/java/ch/puzzle/pcts/controller/MemberOverviewController.java b/backend/src/main/java/ch/puzzle/pcts/controller/MemberOverviewController.java index f6d086192..a54eee092 100644 --- a/backend/src/main/java/ch/puzzle/pcts/controller/MemberOverviewController.java +++ b/backend/src/main/java/ch/puzzle/pcts/controller/MemberOverviewController.java @@ -11,6 +11,7 @@ import io.swagger.v3.oas.annotations.media.Schema; import io.swagger.v3.oas.annotations.responses.ApiResponse; import io.swagger.v3.oas.annotations.tags.Tag; +import java.util.List; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; @@ -35,7 +36,7 @@ public MemberOverviewController(MemberOverviewMapper mapper, MemberOverviewBusin @GetMapping("{memberOverviewId}") public ResponseEntity getMemberOverviewById(@Parameter(description = "ID of the member-overview to retrieve.", required = true) @PathVariable Long memberOverviewId) { - MemberOverview memberOverview = service.getById(memberOverviewId); - return ResponseEntity.ok(mapper.toDto(memberOverview)); + List memberOverviews = service.getById(memberOverviewId); + return ResponseEntity.ok(mapper.toDto(memberOverviews)); } } diff --git a/backend/src/main/java/ch/puzzle/pcts/mapper/MemberOverviewMapper.java b/backend/src/main/java/ch/puzzle/pcts/mapper/MemberOverviewMapper.java index b6ad76756..c002964bb 100644 --- a/backend/src/main/java/ch/puzzle/pcts/mapper/MemberOverviewMapper.java +++ b/backend/src/main/java/ch/puzzle/pcts/mapper/MemberOverviewMapper.java @@ -1,24 +1,162 @@ package ch.puzzle.pcts.mapper; +import ch.puzzle.pcts.dto.calculation.CalculationDto; +import ch.puzzle.pcts.dto.certificate.CertificateDto; +import ch.puzzle.pcts.dto.certificatetype.CertificateTypeDto; +import ch.puzzle.pcts.dto.degree.DegreeDto; +import ch.puzzle.pcts.dto.degreetype.DegreeTypeDto; +import ch.puzzle.pcts.dto.experience.ExperienceDto; +import ch.puzzle.pcts.dto.experiencetype.ExperienceTypeDto; +import ch.puzzle.pcts.dto.leadershipexperience.LeadershipExperienceDto; +import ch.puzzle.pcts.dto.leadershipexperiencetype.LeadershipExperienceTypeDto; +import ch.puzzle.pcts.dto.member.MemberDto; +import ch.puzzle.pcts.dto.memberoverview.MemberCvDto; import ch.puzzle.pcts.dto.memberoverview.MemberOverviewDto; +import ch.puzzle.pcts.dto.organisationunit.OrganisationUnitDto; +import ch.puzzle.pcts.dto.role.RoleDto; import ch.puzzle.pcts.model.memberoverview.MemberOverview; -import com.fasterxml.jackson.databind.ObjectMapper; +import java.util.*; +import java.util.stream.Collectors; import org.springframework.stereotype.Component; @Component public class MemberOverviewMapper { - private final ObjectMapper mapper; - - public MemberOverviewMapper(ObjectMapper mapper) { - this.mapper = mapper; + public MemberOverviewMapper() { } - public MemberOverviewDto toDto(MemberOverview entity) { - try { - return mapper.readValue(entity.getOverview(), MemberOverviewDto.class); - } catch (Exception e) { - throw new IllegalStateException("Failed to map overview JSON", e); - } + public MemberOverviewDto toDto(List entities) { + + MemberOverview first = entities.get(0); + + MemberDto memberDto = new MemberDto(first.getId(), + first.getFirstName(), + first.getLastName(), + first.getEmploymentState(), + first.getAbbreviation(), + first.getDateOfHire(), + first.getBirthDate(), + new OrganisationUnitDto(first.getOrganisationUnitId(), + first.getOrganisationUnitName())); + + List degrees = entities + .stream() + .filter(e -> e.getDegreeId() != null) + .collect(Collectors.groupingBy(MemberOverview::getDegreeId)) + .values() + .stream() + .map(group -> { + MemberOverview e = group.getFirst(); + + return new DegreeDto(e.getDegreeId(), + memberDto, + e.getDegreeName(), + e.getDegreeInstitution(), + e.getDegreeCompleted(), + new DegreeTypeDto(e.getDegreeTypeId(), + e.getDegreeTypeName(), + e.getDegreeHighlyRelevantPoints(), + e.getDegreeLimitedRelevantPoints(), + e.getDegreeLittleRelevantPoints()), + e.getDegreeStartDate(), + e.getDegreeEndDate(), + e.getDegreeComment()); + }) + .toList(); + + List experiences = entities + .stream() + .filter(e -> e.getExperienceId() != null) + .collect(Collectors.groupingBy(MemberOverview::getExperienceId)) + .values() + .stream() + .map(group -> { + MemberOverview e = group.getFirst(); + + return new ExperienceDto(e.getExperienceId(), + memberDto, + e.getExperienceName(), + e.getExperienceEmployer(), + e.getExperiencePercent(), + new ExperienceTypeDto(e.getExperienceTypeId(), + e.getExperienceTypeName(), + e.getExperienceHighlyRelevantPoints(), + e.getExperienceLimitedRelevantPoints(), + e.getExperienceLittleRelevantPoints()), + e.getExperienceComment(), + e.getExperienceStartDate(), + e.getExperienceEndDate()); + }) + .toList(); + + Map> certificateGroups = entities + .stream() + .filter(e -> e.getCertificateId() != null && "CERTIFICATE".equals(e.getCertificateKind())) + .collect(Collectors.groupingBy(MemberOverview::getCertificateId)); + + List certificates = certificateGroups.values().stream().map(certRows -> { + + MemberOverview certData = certRows.get(0); + + List tags = certRows + .stream() + .map(MemberOverview::getTagName) + .filter(Objects::nonNull) + .distinct() + .toList(); + + return new CertificateDto(certData.getCertificateId(), + memberDto, + new CertificateTypeDto(certData.getCertificateTypeId(), + certData.getCertificateTypeName(), + certData.getCertificateTypePoints(), + certData.getCertificateTypeComment(), + tags), + certData.getCertificateCompletedAt(), + certData.getCertificateValidUntil(), + certData.getCertificateComment()); + }).toList(); + + List leadershipExperiences = entities + .stream() + .filter(e -> e.getLeadershipId() != null) + .collect(Collectors.groupingBy(MemberOverview::getLeadershipId)) + .values() + .stream() + .map(group -> { + MemberOverview e = group.getFirst(); + + return new LeadershipExperienceDto(e.getLeadershipId(), + memberDto, + new LeadershipExperienceTypeDto(e.getLeadershipTypeId(), + e.getLeadershipTypeName(), + e.getLeadershipTypePoints(), + e.getLeadershipTypeComment(), + e.getLeadershipKind()), + e.getLeadershipComment()); + }) + .toList(); + + List calculations = entities + .stream() + .filter(e -> e.getCalculationId() != null) + .collect(Collectors.groupingBy(MemberOverview::getCalculationId)) + .values() + .stream() + .map(group -> { + MemberOverview e = group.getFirst(); + + return new CalculationDto(e.getCalculationId(), + memberDto, + new RoleDto(e.getRoleId(), e.getRoleName(), e.getRoleIsManagement()), + e.getCalculationState(), + e.getCalculationPublicationDate(), + e.getCalculationPublicizedBy()); + }) + .toList(); + + MemberCvDto cvDto = new MemberCvDto(degrees, experiences, certificates, leadershipExperiences); + + return new MemberOverviewDto(memberDto, cvDto, calculations); } } diff --git a/backend/src/main/java/ch/puzzle/pcts/model/memberoverview/MemberOverview.java b/backend/src/main/java/ch/puzzle/pcts/model/memberoverview/MemberOverview.java index 6cda575b7..706071e6c 100644 --- a/backend/src/main/java/ch/puzzle/pcts/model/memberoverview/MemberOverview.java +++ b/backend/src/main/java/ch/puzzle/pcts/model/memberoverview/MemberOverview.java @@ -1,32 +1,1007 @@ package ch.puzzle.pcts.model.memberoverview; import ch.puzzle.pcts.model.Model; -import jakarta.persistence.Column; -import jakarta.persistence.Entity; -import jakarta.persistence.Id; -import jakarta.persistence.Table; +import ch.puzzle.pcts.model.calculation.CalculationState; +import ch.puzzle.pcts.model.certificatetype.CertificateKind; +import ch.puzzle.pcts.model.member.EmploymentState; +import jakarta.persistence.*; +import java.math.BigDecimal; +import java.time.LocalDate; @Entity @Table(name = "member_overview_view") public class MemberOverview implements Model { @Id - @Column(name = "member_id") - private Long id; + private Long memberId; - @Column(columnDefinition = "jsonb") - private String overview; + private String firstName; + private String lastName; + private String abbreviation; + private LocalDate birthDate; + private LocalDate dateOfHire; + @Enumerated(EnumType.STRING) + private EmploymentState employmentState; + + private Long organisationUnitId; + private String organisationUnitName; + + private Long certificateId; + private LocalDate certificateCompletedAt; + private LocalDate certificateValidUntil; + private String certificateComment; + + private Long certificateTypeId; + private String certificateTypeName; + private BigDecimal certificateTypePoints; + private String certificateTypeComment; + private String certificateKind; + + private Long tagId; + private String tagName; + + private Long leadershipId; + private String leadershipComment; + + private Long degreeId; + private String degreeName; + private LocalDate degreeStartDate; + private LocalDate degreeEndDate; + private String degreeInstitution; + private Boolean degreeCompleted; + private String degreeComment; + private Long leadershipTypeId; + private String leadershipTypeName; + + @Enumerated(EnumType.STRING) + private CertificateKind leadershipKind; + private BigDecimal leadershipTypePoints; + private String leadershipTypeComment; + + private Long degreeTypeId; + private String degreeTypeName; + private BigDecimal degreeHighlyRelevantPoints; + private BigDecimal degreeLimitedRelevantPoints; + private BigDecimal degreeLittleRelevantPoints; + + private Long experienceId; + private String experienceName; + private String experienceEmployer; + private LocalDate experienceStartDate; + private LocalDate experienceEndDate; + private Integer experiencePercent; + private String experienceComment; + + private Long experienceTypeId; + private String experienceTypeName; + private BigDecimal experienceHighlyRelevantPoints; + private BigDecimal experienceLimitedRelevantPoints; + private BigDecimal experienceLittleRelevantPoints; + + private Long calculationId; + @Enumerated(EnumType.STRING) + private CalculationState calculationState; + private LocalDate calculationPublicationDate; + private String calculationPublicizedBy; + + private Long roleId; + private String roleName; + private Boolean roleIsManagement; + + private MemberOverview(Builder builder) { + memberId = builder.memberId; + firstName = builder.firstName; + lastName = builder.lastName; + abbreviation = builder.abbreviation; + birthDate = builder.birthDate; + dateOfHire = builder.dateOfHire; + employmentState = builder.employmentState; + organisationUnitId = builder.organisationUnitId; + organisationUnitName = builder.organisationUnitName; + certificateId = builder.certificateId; + certificateCompletedAt = builder.certificateCompletedAt; + certificateValidUntil = builder.certificateValidUntil; + certificateComment = builder.certificateComment; + certificateTypeId = builder.certificateTypeId; + certificateTypeName = builder.certificateTypeName; + certificateTypePoints = builder.certificateTypePoints; + certificateTypeComment = builder.certificateTypeComment; + certificateKind = builder.certificateKind; + tagId = builder.tagId; + tagName = builder.tagName; + leadershipId = builder.leadershipId; + leadershipComment = builder.leadershipComment; + degreeId = builder.degreeId; + degreeName = builder.degreeName; + degreeStartDate = builder.degreeStartDate; + degreeEndDate = builder.degreeEndDate; + degreeInstitution = builder.degreeInstitution; + degreeCompleted = builder.degreeCompleted; + degreeComment = builder.degreeComment; + leadershipTypeId = builder.leadershipTypeId; + leadershipTypeName = builder.leadershipTypeName; + leadershipKind = builder.leadershipKind; + leadershipTypePoints = builder.leadershipTypePoints; + leadershipTypeComment = builder.leadershipTypeComment; + degreeTypeId = builder.degreeTypeId; + degreeTypeName = builder.degreeTypeName; + degreeHighlyRelevantPoints = builder.degreeHighlyRelevantPoints; + degreeLimitedRelevantPoints = builder.degreeLimitedRelevantPoints; + degreeLittleRelevantPoints = builder.degreeLittleRelevantPoints; + experienceId = builder.experienceId; + experienceName = builder.experienceName; + experienceEmployer = builder.experienceEmployer; + experienceStartDate = builder.experienceStartDate; + experienceEndDate = builder.experienceEndDate; + experiencePercent = builder.experiencePercent; + experienceComment = builder.experienceComment; + experienceTypeId = builder.experienceTypeId; + experienceTypeName = builder.experienceTypeName; + experienceHighlyRelevantPoints = builder.experienceHighlyRelevantPoints; + experienceLimitedRelevantPoints = builder.experienceLimitedRelevantPoints; + experienceLittleRelevantPoints = builder.experienceLittleRelevantPoints; + calculationId = builder.calculationId; + calculationState = builder.calculationState; + calculationPublicationDate = builder.calculationPublicationDate; + calculationPublicizedBy = builder.calculationPublicizedBy; + roleId = builder.roleId; + roleName = builder.roleName; + roleIsManagement = builder.roleIsManagement; + } + + public MemberOverview() { + + } + + public String getFirstName() { + return firstName; + } + + public void setFirstName(String firstName) { + this.firstName = firstName; + } + + public String getLastName() { + return lastName; + } + + public void setLastName(String lastName) { + this.lastName = lastName; + } + + public String getAbbreviation() { + return abbreviation; + } + + public void setAbbreviation(String abbreviation) { + this.abbreviation = abbreviation; + } + + public LocalDate getBirthDate() { + return birthDate; + } + + public void setBirthDate(LocalDate birthDate) { + this.birthDate = birthDate; + } + + public LocalDate getDateOfHire() { + return dateOfHire; + } + + public void setDateOfHire(LocalDate dateOfHire) { + this.dateOfHire = dateOfHire; + } + + public EmploymentState getEmploymentState() { + return employmentState; + } + + public void setEmploymentState(EmploymentState employmentState) { + this.employmentState = employmentState; + } + + public Long getOrganisationUnitId() { + return organisationUnitId; + } + + public void setOrganisationUnitId(Long organisationUnitId) { + this.organisationUnitId = organisationUnitId; + } + + public String getOrganisationUnitName() { + return organisationUnitName; + } + + public void setOrganisationUnitName(String organisationUnitName) { + this.organisationUnitName = organisationUnitName; + } + + public Long getCertificateId() { + return certificateId; + } + + public void setCertificateId(Long certificateId) { + this.certificateId = certificateId; + } + + public LocalDate getCertificateCompletedAt() { + return certificateCompletedAt; + } + + public void setCertificateCompletedAt(LocalDate certificateCompletedAt) { + this.certificateCompletedAt = certificateCompletedAt; + } + + public LocalDate getCertificateValidUntil() { + return certificateValidUntil; + } + + public void setCertificateValidUntil(LocalDate certificateValidUntil) { + this.certificateValidUntil = certificateValidUntil; + } + + public String getCertificateComment() { + return certificateComment; + } + + public void setCertificateComment(String certificateComment) { + this.certificateComment = certificateComment; + } + + public Long getCertificateTypeId() { + return certificateTypeId; + } + + public void setCertificateTypeId(Long certificateTypeId) { + this.certificateTypeId = certificateTypeId; + } + + public String getCertificateTypeName() { + return certificateTypeName; + } + + public void setCertificateTypeName(String certificateTypeName) { + this.certificateTypeName = certificateTypeName; + } + + public BigDecimal getCertificateTypePoints() { + return certificateTypePoints; + } + + public void setCertificateTypePoints(BigDecimal certificateTypePoints) { + this.certificateTypePoints = certificateTypePoints; + } + + public String getCertificateTypeComment() { + return certificateTypeComment; + } + + public void setCertificateTypeComment(String certificateTypeComment) { + this.certificateTypeComment = certificateTypeComment; + } + + public String getCertificateKind() { + return certificateKind; + } + + public void setCertificateKind(String certificateKind) { + this.certificateKind = certificateKind; + } + + public Long getTagId() { + return tagId; + } + + public void setTagId(Long tagId) { + this.tagId = tagId; + } + + public String getTagName() { + return tagName; + } + + public void setTagName(String tagName) { + this.tagName = tagName; + } + + public Long getLeadershipId() { + return leadershipId; + } + + public void setLeadershipId(Long leadershipId) { + this.leadershipId = leadershipId; + } + + public String getLeadershipComment() { + return leadershipComment; + } + + public void setLeadershipComment(String leadershipComment) { + this.leadershipComment = leadershipComment; + } + + public Long getDegreeId() { + return degreeId; + } + + public void setDegreeId(Long degreeId) { + this.degreeId = degreeId; + } + + public String getDegreeName() { + return degreeName; + } + + public void setDegreeName(String degreeName) { + this.degreeName = degreeName; + } + + public LocalDate getDegreeStartDate() { + return degreeStartDate; + } + + public void setDegreeStartDate(LocalDate degreeStartDate) { + this.degreeStartDate = degreeStartDate; + } + + public LocalDate getDegreeEndDate() { + return degreeEndDate; + } + + public void setDegreeEndDate(LocalDate degreeEndDate) { + this.degreeEndDate = degreeEndDate; + } + + public String getDegreeInstitution() { + return degreeInstitution; + } + + public void setDegreeInstitution(String degreeInstitution) { + this.degreeInstitution = degreeInstitution; + } + + public Boolean getDegreeCompleted() { + return degreeCompleted; + } + + public void setDegreeCompleted(Boolean degreeCompleted) { + this.degreeCompleted = degreeCompleted; + } + + public String getDegreeComment() { + return degreeComment; + } + + public void setDegreeComment(String degreeComment) { + this.degreeComment = degreeComment; + } + + public Long getLeadershipTypeId() { + return leadershipTypeId; + } + + public void setLeadershipTypeId(Long leadershipTypeId) { + this.leadershipTypeId = leadershipTypeId; + } + + public String getLeadershipTypeName() { + return leadershipTypeName; + } + + public void setLeadershipTypeName(String leadershipTypeName) { + this.leadershipTypeName = leadershipTypeName; + } + + public CertificateKind getLeadershipKind() { + return leadershipKind; + } + + public void setLeadershipKind(CertificateKind leadershipKind) { + this.leadershipKind = leadershipKind; + } + + public BigDecimal getLeadershipTypePoints() { + return leadershipTypePoints; + } + + public void setLeadershipTypePoints(BigDecimal leadershipTypePoints) { + this.leadershipTypePoints = leadershipTypePoints; + } + + public String getLeadershipTypeComment() { + return leadershipTypeComment; + } + + public void setLeadershipTypeComment(String leadershipTypeComment) { + this.leadershipTypeComment = leadershipTypeComment; + } + + public Long getDegreeTypeId() { + return degreeTypeId; + } + + public void setDegreeTypeId(Long degreeTypeId) { + this.degreeTypeId = degreeTypeId; + } + + public String getDegreeTypeName() { + return degreeTypeName; + } + + public void setDegreeTypeName(String degreeTypeName) { + this.degreeTypeName = degreeTypeName; + } + + public BigDecimal getDegreeHighlyRelevantPoints() { + return degreeHighlyRelevantPoints; + } + + public void setDegreeHighlyRelevantPoints(BigDecimal degreeHighlyRelevantPoints) { + this.degreeHighlyRelevantPoints = degreeHighlyRelevantPoints; + } + + public BigDecimal getDegreeLimitedRelevantPoints() { + return degreeLimitedRelevantPoints; + } + + public void setDegreeLimitedRelevantPoints(BigDecimal degreeLimitedRelevantPoints) { + this.degreeLimitedRelevantPoints = degreeLimitedRelevantPoints; + } + + public BigDecimal getDegreeLittleRelevantPoints() { + return degreeLittleRelevantPoints; + } + + public void setDegreeLittleRelevantPoints(BigDecimal degreeLittleRelevantPoints) { + this.degreeLittleRelevantPoints = degreeLittleRelevantPoints; + } + + public Long getExperienceId() { + return experienceId; + } + + public void setExperienceId(Long experienceId) { + this.experienceId = experienceId; + } + + public String getExperienceName() { + return experienceName; + } + + public void setExperienceName(String experienceName) { + this.experienceName = experienceName; + } + + public String getExperienceEmployer() { + return experienceEmployer; + } + + public void setExperienceEmployer(String experienceEmployer) { + this.experienceEmployer = experienceEmployer; + } + + public LocalDate getExperienceStartDate() { + return experienceStartDate; + } + + public void setExperienceStartDate(LocalDate experienceStartDate) { + this.experienceStartDate = experienceStartDate; + } + + public LocalDate getExperienceEndDate() { + return experienceEndDate; + } + + public void setExperienceEndDate(LocalDate experienceEndDate) { + this.experienceEndDate = experienceEndDate; + } + + public Integer getExperiencePercent() { + return experiencePercent; + } + + public void setExperiencePercent(Integer experiencePercent) { + this.experiencePercent = experiencePercent; + } + + public String getExperienceComment() { + return experienceComment; + } + + public void setExperienceComment(String experienceComment) { + this.experienceComment = experienceComment; + } + + public Long getExperienceTypeId() { + return experienceTypeId; + } + + public void setExperienceTypeId(Long experienceTypeId) { + this.experienceTypeId = experienceTypeId; + } + + public String getExperienceTypeName() { + return experienceTypeName; + } + + public void setExperienceTypeName(String experienceTypeName) { + this.experienceTypeName = experienceTypeName; + } + + public BigDecimal getExperienceHighlyRelevantPoints() { + return experienceHighlyRelevantPoints; + } + + public void setExperienceHighlyRelevantPoints(BigDecimal experienceHighlyRelevantPoints) { + this.experienceHighlyRelevantPoints = experienceHighlyRelevantPoints; + } + + public BigDecimal getExperienceLimitedRelevantPoints() { + return experienceLimitedRelevantPoints; + } + + public void setExperienceLimitedRelevantPoints(BigDecimal experienceLimitedRelevantPoints) { + this.experienceLimitedRelevantPoints = experienceLimitedRelevantPoints; + } + + public BigDecimal getExperienceLittleRelevantPoints() { + return experienceLittleRelevantPoints; + } + + public void setExperienceLittleRelevantPoints(BigDecimal experienceLittleRelevantPoints) { + this.experienceLittleRelevantPoints = experienceLittleRelevantPoints; + } + + public Long getCalculationId() { + return calculationId; + } + + public void setCalculationId(Long calculationId) { + this.calculationId = calculationId; + } + + public CalculationState getCalculationState() { + return calculationState; + } + + public void setCalculationState(CalculationState calculationState) { + this.calculationState = calculationState; + } + + public LocalDate getCalculationPublicationDate() { + return calculationPublicationDate; + } + + public void setCalculationPublicationDate(LocalDate calculationPublicationDate) { + this.calculationPublicationDate = calculationPublicationDate; + } + + public String getCalculationPublicizedBy() { + return calculationPublicizedBy; + } + + public void setCalculationPublicizedBy(String calculationPublicizedBy) { + this.calculationPublicizedBy = calculationPublicizedBy; + } + + public Long getRoleId() { + return roleId; + } + + public void setRoleId(Long roleId) { + this.roleId = roleId; + } + + public String getRoleName() { + return roleName; + } + + public void setRoleName(String roleName) { + this.roleName = roleName; + } + + public Boolean getRoleIsManagement() { + return roleIsManagement; + } + + public void setRoleIsManagement(Boolean roleIsManagement) { + this.roleIsManagement = roleIsManagement; + } + + @Override + public int hashCode() { + return super.hashCode(); + } + + @Override + public boolean equals(Object obj) { + return super.equals(obj); + } + + @Override + public String toString() { + return super.toString(); + } + + @Override public Long getId() { - return id; + return memberId; } @Override public void setId(Long id) { - this.id = id; + } - public String getOverview() { - return overview; + public static final class Builder { + private Long memberId; + private String firstName; + private String lastName; + private String abbreviation; + private LocalDate birthDate; + private LocalDate dateOfHire; + private EmploymentState employmentState; + + private Long organisationUnitId; + private String organisationUnitName; + + private Long certificateId; + private LocalDate certificateCompletedAt; + private LocalDate certificateValidUntil; + private String certificateComment; + + private Long certificateTypeId; + private String certificateTypeName; + private BigDecimal certificateTypePoints; + private String certificateTypeComment; + private String certificateKind; + + private Long tagId; + private String tagName; + + private Long leadershipId; + private String leadershipComment; + + private Long degreeId; + private String degreeName; + private LocalDate degreeStartDate; + private LocalDate degreeEndDate; + private String degreeInstitution; + private Boolean degreeCompleted; + private String degreeComment; + + private Long leadershipTypeId; + private String leadershipTypeName; + private CertificateKind leadershipKind; + private BigDecimal leadershipTypePoints; + private String leadershipTypeComment; + + private Long degreeTypeId; + private String degreeTypeName; + private BigDecimal degreeHighlyRelevantPoints; + private BigDecimal degreeLimitedRelevantPoints; + private BigDecimal degreeLittleRelevantPoints; + + private Long experienceId; + private String experienceName; + private String experienceEmployer; + private LocalDate experienceStartDate; + private LocalDate experienceEndDate; + private Integer experiencePercent; + private String experienceComment; + + private Long experienceTypeId; + private String experienceTypeName; + private BigDecimal experienceHighlyRelevantPoints; + private BigDecimal experienceLimitedRelevantPoints; + private BigDecimal experienceLittleRelevantPoints; + + private Long calculationId; + private CalculationState calculationState; + private LocalDate calculationPublicationDate; + private String calculationPublicizedBy; + + private Long roleId; + private String roleName; + private Boolean roleIsManagement; + + private Builder() { + } + + public static Builder builder() { + return new Builder(); + } + + public Builder withMemberId(Long memberId) { + this.memberId = memberId; + return this; + } + + public Builder withFirstName(String firstName) { + this.firstName = firstName; + return this; + } + + public Builder withLastName(String lastName) { + this.lastName = lastName; + return this; + } + + public Builder withAbbreviation(String abbreviation) { + this.abbreviation = abbreviation; + return this; + } + + public Builder withBirthDate(LocalDate birthDate) { + this.birthDate = birthDate; + return this; + } + + public Builder withDateOfHire(LocalDate dateOfHire) { + this.dateOfHire = dateOfHire; + return this; + } + + public Builder withEmploymentState(EmploymentState employmentState) { + this.employmentState = employmentState; + return this; + } + + public Builder withOrganisationUnitId(Long organisationUnitId) { + this.organisationUnitId = organisationUnitId; + return this; + } + + public Builder withOrganisationUnitName(String organisationUnitName) { + this.organisationUnitName = organisationUnitName; + return this; + } + + public Builder withCertificateId(Long certificateId) { + this.certificateId = certificateId; + return this; + } + + public Builder withCertificateCompletedAt(LocalDate certificateCompletedAt) { + this.certificateCompletedAt = certificateCompletedAt; + return this; + } + + public Builder withCertificateValidUntil(LocalDate certificateValidUntil) { + this.certificateValidUntil = certificateValidUntil; + return this; + } + + public Builder withCertificateComment(String certificateComment) { + this.certificateComment = certificateComment; + return this; + } + + public Builder withCertificateTypeId(Long certificateTypeId) { + this.certificateTypeId = certificateTypeId; + return this; + } + + public Builder withCertificateTypeName(String certificateTypeName) { + this.certificateTypeName = certificateTypeName; + return this; + } + + public Builder withCertificateTypePoints(BigDecimal certificateTypePoints) { + this.certificateTypePoints = certificateTypePoints; + return this; + } + + public Builder withCertificateTypeComment(String certificateTypeComment) { + this.certificateTypeComment = certificateTypeComment; + return this; + } + + public Builder withCertificateKind(String certificateKind) { + this.certificateKind = certificateKind; + return this; + } + + public Builder withTagId(Long tagId) { + this.tagId = tagId; + return this; + } + + public Builder withTagName(String tagName) { + this.tagName = tagName; + return this; + } + + public Builder withLeadershipId(Long leadershipId) { + this.leadershipId = leadershipId; + return this; + } + + public Builder withLeadershipComment(String leadershipComment) { + this.leadershipComment = leadershipComment; + return this; + } + + public Builder withLeadershipTypeId(Long leadershipTypeId) { + this.leadershipTypeId = leadershipTypeId; + return this; + } + + public Builder withLeadershipTypeName(String leadershipTypeName) { + this.leadershipTypeName = leadershipTypeName; + return this; + } + + public Builder withLeadershipKind(CertificateKind leadershipKind) { + this.leadershipKind = leadershipKind; + return this; + } + + public Builder withLeadershipTypePoints(BigDecimal leadershipTypePoints) { + this.leadershipTypePoints = leadershipTypePoints; + return this; + } + + public Builder withLeadershipTypeComment(String leadershipTypeComment) { + this.leadershipTypeComment = leadershipTypeComment; + return this; + } + + public Builder withDegreeId(Long degreeId) { + this.degreeId = degreeId; + return this; + } + + public Builder withDegreeName(String degreeName) { + this.degreeName = degreeName; + return this; + } + + public Builder withDegreeStartDate(LocalDate degreeStartDate) { + this.degreeStartDate = degreeStartDate; + return this; + } + + public Builder withDegreeEndDate(LocalDate degreeEndDate) { + this.degreeEndDate = degreeEndDate; + return this; + } + + public Builder withDegreeInstitution(String degreeInstitution) { + this.degreeInstitution = degreeInstitution; + return this; + } + + public Builder withDegreeCompleted(Boolean degreeCompleted) { + this.degreeCompleted = degreeCompleted; + return this; + } + + public Builder withDegreeComment(String degreeComment) { + this.degreeComment = degreeComment; + return this; + } + + public Builder withDegreeTypeId(Long degreeTypeId) { + this.degreeTypeId = degreeTypeId; + return this; + } + + public Builder withDegreeTypeName(String degreeTypeName) { + this.degreeTypeName = degreeTypeName; + return this; + } + + public Builder withDegreeHighlyRelevantPoints(BigDecimal degreeHighlyRelevantPoints) { + this.degreeHighlyRelevantPoints = degreeHighlyRelevantPoints; + return this; + } + + public Builder withDegreeLimitedRelevantPoints(BigDecimal degreeLimitedRelevantPoints) { + this.degreeLimitedRelevantPoints = degreeLimitedRelevantPoints; + return this; + } + + public Builder withDegreeLittleRelevantPoints(BigDecimal degreeLittleRelevantPoints) { + this.degreeLittleRelevantPoints = degreeLittleRelevantPoints; + return this; + } + + public Builder withExperienceId(Long experienceId) { + this.experienceId = experienceId; + return this; + } + + public Builder withExperienceName(String experienceName) { + this.experienceName = experienceName; + return this; + } + + public Builder withExperienceEmployer(String experienceEmployer) { + this.experienceEmployer = experienceEmployer; + return this; + } + + public Builder withExperienceStartDate(LocalDate experienceStartDate) { + this.experienceStartDate = experienceStartDate; + return this; + } + + public Builder withExperienceEndDate(LocalDate experienceEndDate) { + this.experienceEndDate = experienceEndDate; + return this; + } + + public Builder withExperiencePercent(Integer experiencePercent) { + this.experiencePercent = experiencePercent; + return this; + } + + public Builder withExperienceComment(String experienceComment) { + this.experienceComment = experienceComment; + return this; + } + + public Builder withExperienceTypeId(Long experienceTypeId) { + this.experienceTypeId = experienceTypeId; + return this; + } + + public Builder withExperienceTypeName(String experienceTypeName) { + this.experienceTypeName = experienceTypeName; + return this; + } + + public Builder withExperienceHighlyRelevantPoints(BigDecimal experienceHighlyRelevantPoints) { + this.experienceHighlyRelevantPoints = experienceHighlyRelevantPoints; + return this; + } + + public Builder withExperienceLimitedRelevantPoints(BigDecimal experienceLimitedRelevantPoints) { + this.experienceLimitedRelevantPoints = experienceLimitedRelevantPoints; + return this; + } + + public Builder withExperienceLittleRelevantPoints(BigDecimal experienceLittleRelevantPoints) { + this.experienceLittleRelevantPoints = experienceLittleRelevantPoints; + return this; + } + + public Builder withCalculationId(Long calculationId) { + this.calculationId = calculationId; + return this; + } + + public Builder withCalculationState(CalculationState calculationState) { + this.calculationState = calculationState; + return this; + } + + public Builder withCalculationPublicationDate(LocalDate calculationPublicationDate) { + this.calculationPublicationDate = calculationPublicationDate; + return this; + } + + public Builder withCalculationPublicizedBy(String calculationPublicizedBy) { + this.calculationPublicizedBy = calculationPublicizedBy; + return this; + } + + public Builder withRoleId(Long roleId) { + this.roleId = roleId; + return this; + } + + public Builder withRoleName(String roleName) { + this.roleName = roleName; + return this; + } + + public Builder withRoleIsManagement(Boolean roleIsManagement) { + this.roleIsManagement = roleIsManagement; + return this; + } } } diff --git a/backend/src/main/java/ch/puzzle/pcts/repository/MemberOverviewRepository.java b/backend/src/main/java/ch/puzzle/pcts/repository/MemberOverviewRepository.java index c6ae67bdb..d22bf03e2 100644 --- a/backend/src/main/java/ch/puzzle/pcts/repository/MemberOverviewRepository.java +++ b/backend/src/main/java/ch/puzzle/pcts/repository/MemberOverviewRepository.java @@ -1,11 +1,11 @@ package ch.puzzle.pcts.repository; import ch.puzzle.pcts.model.memberoverview.MemberOverview; -import java.util.Optional; +import java.util.List; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.stereotype.Repository; @Repository public interface MemberOverviewRepository extends JpaRepository { - Optional findOverviewById(Long id); + List findAllByMemberId(Long id); } diff --git a/backend/src/main/java/ch/puzzle/pcts/service/business/MemberOverviewBusinessService.java b/backend/src/main/java/ch/puzzle/pcts/service/business/MemberOverviewBusinessService.java index 7bfac8e16..4c25ea89e 100644 --- a/backend/src/main/java/ch/puzzle/pcts/service/business/MemberOverviewBusinessService.java +++ b/backend/src/main/java/ch/puzzle/pcts/service/business/MemberOverviewBusinessService.java @@ -2,15 +2,9 @@ import static ch.puzzle.pcts.Constants.MEMBER_OVERVIEW; -import ch.puzzle.pcts.dto.error.ErrorKey; -import ch.puzzle.pcts.dto.error.FieldKey; -import ch.puzzle.pcts.dto.error.GenericErrorDto; -import ch.puzzle.pcts.exception.PCTSException; import ch.puzzle.pcts.model.memberoverview.MemberOverview; import ch.puzzle.pcts.service.persistence.MemberOverviewPersistenceService; import java.util.List; -import java.util.Map; -import org.springframework.http.HttpStatus; import org.springframework.stereotype.Service; @Service @@ -22,15 +16,8 @@ public MemberOverviewBusinessService(MemberOverviewPersistenceService persistenc this.persistenceService = persistenceService; } - public MemberOverview getById(Long id) { - return persistenceService.getById(id).orElseThrow(() -> { - Map attributes = Map - .of(FieldKey.ENTITY, entityName(), FieldKey.FIELD, "id", FieldKey.IS, id.toString()); - - GenericErrorDto error = new GenericErrorDto(ErrorKey.NOT_FOUND, attributes); - - return new PCTSException(HttpStatus.NOT_FOUND, List.of(error)); - }); + public List getById(Long id) { + return persistenceService.getById(id); } protected String entityName() { diff --git a/backend/src/main/java/ch/puzzle/pcts/service/persistence/MemberOverviewPersistenceService.java b/backend/src/main/java/ch/puzzle/pcts/service/persistence/MemberOverviewPersistenceService.java index 12033d051..30aff2afb 100644 --- a/backend/src/main/java/ch/puzzle/pcts/service/persistence/MemberOverviewPersistenceService.java +++ b/backend/src/main/java/ch/puzzle/pcts/service/persistence/MemberOverviewPersistenceService.java @@ -2,7 +2,7 @@ import ch.puzzle.pcts.model.memberoverview.MemberOverview; import ch.puzzle.pcts.repository.MemberOverviewRepository; -import java.util.Optional; +import java.util.List; import org.springframework.stereotype.Service; @Service @@ -14,7 +14,7 @@ public MemberOverviewPersistenceService(MemberOverviewRepository repository) { this.repository = repository; } - public Optional getById(Long id) { - return repository.findOverviewById(id); + public List getById(Long id) { + return repository.findAllByMemberId(id); } } diff --git a/backend/src/main/resources/db/migration/V0_0_14__add_member_view.sql b/backend/src/main/resources/db/migration/V0_0_14__add_member_view.sql index 73183a302..1aa3fb9ce 100644 --- a/backend/src/main/resources/db/migration/V0_0_14__add_member_view.sql +++ b/backend/src/main/resources/db/migration/V0_0_14__add_member_view.sql @@ -1,161 +1,185 @@ -CREATE OR REPLACE FUNCTION build_member_overview(p_member_id BIGINT) - RETURNS JSONB - LANGUAGE sql - STABLE -AS $$ -WITH base AS ( - SELECT * - FROM member m - WHERE m.id = p_member_id -), - - cert AS ( - SELECT jsonb_agg( - jsonb_build_object( - 'id', c.id, - 'certificate', jsonb_build_object( - 'id', ct.id, - 'name', ct.name, - 'points', ct.points, - 'comment', ct.comment - ), - 'completedAt', c.completed_at, - 'validUntil', c.valid_until, - 'comment', c.comment - ) - ORDER BY c.completed_at - ) AS certificates - FROM certificate c - LEFT JOIN certificate_type ct ON ct.id = c.certificate_type_id - WHERE c.member_id = p_member_id - AND c.deleted_at IS NULL - AND ct.certificate_kind = 'CERTIFICATE' - ), - - leader AS ( - SELECT jsonb_agg( - jsonb_build_object( - 'id', c.id, - 'comment', c.comment, - 'experience', jsonb_build_object( - 'id', ct.id, - 'name', ct.name, - 'experienceKind', ct.certificate_kind, - 'points', ct.points, - 'comment', ct.comment - ) - ) - ORDER BY c.completed_at - ) AS leadership_experiences - FROM certificate c - LEFT JOIN certificate_type ct ON ct.id = c.certificate_type_id - WHERE c.member_id = p_member_id - AND c.deleted_at IS NULL - AND ct.certificate_kind != 'CERTIFICATE' - ), - - degrees AS ( - SELECT jsonb_agg( - jsonb_build_object( - 'id', d.id, - 'name', d.name, - 'startDate', d.start_date, - 'endDate', d.end_date, - 'institution', d.institution, - 'completed', d.completed, - 'type', jsonb_build_object( - 'degree_type_id', dt.id, - 'name', dt.name, - 'highlyRelevantPoints', dt.highly_relevant_points, - 'limitedRelevantPoints', dt.limited_relevant_points, - 'littleRelevantPoints', dt.little_relevant_points - ) - ) - ORDER BY d.start_date - ) AS degrees - FROM degree d - LEFT JOIN degree_type dt ON dt.id = d.degree_type_id - WHERE d.member_id = p_member_id - ), - - experience AS ( - SELECT jsonb_agg( - jsonb_build_object( - 'id', e.id, - 'name', e.name, - 'employer', e.employer, - 'startDate', e.start_date, - 'endDate', e.end_date, - 'percent', e.percent, - 'comment', e.comment, - 'type', jsonb_build_object( - 'id', et.id, - 'name', et.name, - 'highlyRelevantPoints', et.highly_relevant_points, - 'limitedRelevantPoints', et.limited_relevant_points, - 'littleRelevantPoints', et.little_relevant_points - ) - ) - ORDER BY e.start_date - ) AS experiences - FROM experience e - LEFT JOIN experience_type et ON et.id = e.experience_type_id - WHERE e.member_id = p_member_id - ), - - calcs AS ( - SELECT jsonb_agg( - jsonb_build_object( - 'id', c.id, - 'state', c.state, - 'publicationDate', c.publication_date, - 'publicizedBy', c.publicized_by, - 'role', jsonb_build_object( - 'id', r.id, - 'name', r.name, - 'isManagement', r.is_management - ) - ) - ORDER BY c.id - ) AS calculations - FROM calculation c - LEFT JOIN role r ON r.id = c.role_id - WHERE c.member_id = p_member_id - ) - -SELECT jsonb_build_object( - 'member', jsonb_build_object( - 'id', m.id, - 'firstName', m.first_name, - 'lastName', m.last_name, - 'abbreviation', m.abbreviation, - 'dateOfBirth', m.birth_date, - 'dateOfHire', m.date_of_hire, - 'employmentState', m.employment_state, - 'organisationUnit', jsonb_build_object( - 'id', ou.id, - 'name', ou.name - ) - ), - 'cv', jsonb_build_object( - 'degrees', COALESCE(d.degrees, '[]'::jsonb), - 'certificates', COALESCE(c.certificates, '[]'::jsonb), - 'experiences', COALESCE(x.experiences, '[]'::jsonb), - 'leadershipExperiences', COALESCE(l.leadership_experiences, '[]'::jsonb) - ), - 'calculations', COALESCE(ca.calculations, '[]'::jsonb) - ) -FROM base m - LEFT JOIN organisation_unit ou ON ou.id = m.organisation_unit - LEFT JOIN cert c ON TRUE - LEFT JOIN leader l ON TRUE - LEFT JOIN degrees d ON TRUE - LEFT JOIN experience x ON TRUE - LEFT JOIN calcs ca ON TRUE; -$$; - -CREATE OR REPLACE VIEW member_overview_view AS +DROP VIEW IF EXISTS member_overview_view; + +CREATE VIEW member_overview_view AS SELECT m.id AS member_id, - build_member_overview(m.id) AS overview -FROM member m; + m.first_name, + m.last_name, + m.abbreviation, + m.birth_date, + m.date_of_hire, + m.employment_state, + + ou.id AS organisation_unit_id, + ou.name AS organisation_unit_name, + + -- Certificate row (0..1 per certificate_type per row) + cert_row.certificate_id, + cert_row.certificate_completed_at, + cert_row.certificate_valid_until, + cert_row.certificate_comment, + cert_row.certificate_type_id, + cert_row.certificate_type_name, + cert_row.certificate_type_points, + cert_row.certificate_type_comment, + cert_row.certificate_kind, + cert_row.tag_id, + cert_row.tag_name, + + -- Leadership row + leader_row.leadership_id, + leader_row.leadership_comment, + leader_row.leadership_type_id, + leader_row.leadership_type_name, + leader_row.leadership_kind, + leader_row.leadership_type_points, + leader_row.leadership_type_comment, + + -- Degree row + degree_row.degree_id, + degree_row.degree_name, + degree_row.degree_start_date, + degree_row.degree_end_date, + degree_row.degree_institution, + degree_row.degree_completed, + degree_row.degree_comment, + degree_row.degree_type_id, + degree_row.degree_type_name, + degree_row.degree_highly_relevant_points, + degree_row.degree_limited_relevant_points, + degree_row.degree_little_relevant_points, + + -- Experience row + exp_row.experience_id, + exp_row.experience_name, + exp_row.experience_employer, + exp_row.experience_start_date, + exp_row.experience_end_date, + exp_row.experience_percent, + exp_row.experience_comment, + exp_row.experience_type_id, + exp_row.experience_type_name, + exp_row.experience_highly_relevant_points, + exp_row.experience_limited_relevant_points, + exp_row.experience_little_relevant_points, + + -- Calculation row + calc_row.calculation_id, + calc_row.calculation_state, + calc_row.calculation_publication_date, + calc_row.calculation_publicized_by, + calc_row.role_id, + calc_row.role_name, + calc_row.role_is_management + +FROM member m + LEFT JOIN organisation_unit ou + ON ou.id = m.organisation_unit + +-- ------------- CERTIFICATES + TAGS ------------------- + LEFT JOIN LATERAL ( + SELECT + cert.id AS certificate_id, + cert.completed_at AS certificate_completed_at, + cert.valid_until AS certificate_valid_until, + cert.comment AS certificate_comment, + + ct.id AS certificate_type_id, + ct.name AS certificate_type_name, + ct.points AS certificate_type_points, + ct.comment AS certificate_type_comment, + ct.certificate_kind, + + t.id AS tag_id, + t.name AS tag_name + FROM certificate cert + JOIN certificate_type ct + ON ct.id = cert.certificate_type_id + LEFT JOIN certificate_type_tag ctt + ON ctt.certificate_type_id = ct.id + LEFT JOIN tag t + ON t.id = ctt.tag_id + WHERE cert.member_id = m.id + AND cert.deleted_at IS NULL + AND ct.certificate_kind = 'CERTIFICATE' + ) cert_row ON TRUE + +-- ------------- LEADERSHIP EXPERIENCE ------------------- + LEFT JOIN LATERAL ( + SELECT + leader.id AS leadership_id, + leader.comment AS leadership_comment, + + lct.id AS leadership_type_id, + lct.name AS leadership_type_name, + lct.certificate_kind AS leadership_kind, + lct.points AS leadership_type_points, + lct.comment AS leadership_type_comment + FROM certificate leader + JOIN certificate_type lct + ON lct.id = leader.certificate_type_id + WHERE leader.member_id = m.id + AND leader.deleted_at IS NULL + AND lct.certificate_kind != 'CERTIFICATE' + ) leader_row ON TRUE + +-- ------------- DEGREES ------------------- + LEFT JOIN LATERAL ( + SELECT + d.id AS degree_id, + d.name AS degree_name, + d.start_date AS degree_start_date, + d.end_date AS degree_end_date, + d.institution AS degree_institution, + d.completed AS degree_completed, + d.comment AS degree_comment, + + dt.id AS degree_type_id, + dt.name AS degree_type_name, + dt.highly_relevant_points AS degree_highly_relevant_points, + dt.limited_relevant_points AS degree_limited_relevant_points, + dt.little_relevant_points AS degree_little_relevant_points + FROM degree d + LEFT JOIN degree_type dt + ON dt.id = d.degree_type_id + WHERE d.member_id = m.id + ) degree_row ON TRUE + +-- ------------- EXPERIENCES ------------------- + LEFT JOIN LATERAL ( + SELECT + e.id AS experience_id, + e.name AS experience_name, + e.employer AS experience_employer, + e.start_date AS experience_start_date, + e.end_date AS experience_end_date, + e.percent AS experience_percent, + e.comment AS experience_comment, + + et.id AS experience_type_id, + et.name AS experience_type_name, + et.highly_relevant_points AS experience_highly_relevant_points, + et.limited_relevant_points AS experience_limited_relevant_points, + et.little_relevant_points AS experience_little_relevant_points + FROM experience e + LEFT JOIN experience_type et + ON et.id = e.experience_type_id + WHERE e.member_id = m.id + ) exp_row ON TRUE + +-- ------------- CALCULATIONS ------------------- + LEFT JOIN LATERAL ( + SELECT + ca.id AS calculation_id, + ca.state AS calculation_state, + ca.publication_date AS calculation_publication_date, + ca.publicized_by AS calculation_publicized_by, + + r.id AS role_id, + r.name AS role_name, + r.is_management AS role_is_management + FROM calculation ca + LEFT JOIN role r + ON r.id = ca.role_id + WHERE ca.member_id = m.id + ) calc_row ON TRUE; From f1892a9aef6b9d1375939f65ecdc01cd4771af5f Mon Sep 17 00:00:00 2001 From: Manuel Date: Mon, 15 Dec 2025 11:11:43 +0100 Subject: [PATCH 05/30] fix(backend): Re-write view to not collapse every entitiy into one #50 --- .../pcts/mapper/MemberOverviewMapper.java | 2 +- .../model/memberoverview/MemberOverview.java | 39 +- .../db/migration/V0_0_14__add_member_view.sql | 350 +++++++++--------- 3 files changed, 198 insertions(+), 193 deletions(-) diff --git a/backend/src/main/java/ch/puzzle/pcts/mapper/MemberOverviewMapper.java b/backend/src/main/java/ch/puzzle/pcts/mapper/MemberOverviewMapper.java index c002964bb..61ffd9a9c 100644 --- a/backend/src/main/java/ch/puzzle/pcts/mapper/MemberOverviewMapper.java +++ b/backend/src/main/java/ch/puzzle/pcts/mapper/MemberOverviewMapper.java @@ -27,7 +27,7 @@ public MemberOverviewMapper() { public MemberOverviewDto toDto(List entities) { - MemberOverview first = entities.get(0); + MemberOverview first = entities.getFirst(); MemberDto memberDto = new MemberDto(first.getId(), first.getFirstName(), diff --git a/backend/src/main/java/ch/puzzle/pcts/model/memberoverview/MemberOverview.java b/backend/src/main/java/ch/puzzle/pcts/model/memberoverview/MemberOverview.java index 706071e6c..7737465f1 100644 --- a/backend/src/main/java/ch/puzzle/pcts/model/memberoverview/MemberOverview.java +++ b/backend/src/main/java/ch/puzzle/pcts/model/memberoverview/MemberOverview.java @@ -13,8 +13,10 @@ public class MemberOverview implements Model { @Id - private Long memberId; + @Column(name = "unique_row_id") + private Long uniqueRowId; + private Long memberId; private String firstName; private String lastName; private String abbreviation; @@ -90,6 +92,7 @@ public class MemberOverview implements Model { private Boolean roleIsManagement; private MemberOverview(Builder builder) { + uniqueRowId = builder.uniqueRowId; memberId = builder.memberId; firstName = builder.firstName; lastName = builder.lastName; @@ -154,6 +157,24 @@ public MemberOverview() { } + @Override + public Long getId() { + return uniqueRowId; + } + + @Override + public void setId(Long uniqueRowId) { + this.uniqueRowId = uniqueRowId; + } + + public Long getMemberId() { + return memberId; + } + + public void setMemberId(Long memberId) { + this.memberId = memberId; + } + public String getFirstName() { return firstName; } @@ -625,17 +646,8 @@ public String toString() { return super.toString(); } - @Override - public Long getId() { - return memberId; - } - - @Override - public void setId(Long id) { - - } - public static final class Builder { + private Long uniqueRowId; private Long memberId; private String firstName; private String lastName; @@ -714,6 +726,11 @@ public static Builder builder() { return new Builder(); } + public Builder withUniqueRowId(Long uniqueRowId) { + this.uniqueRowId = uniqueRowId; + return this; + } + public Builder withMemberId(Long memberId) { this.memberId = memberId; return this; diff --git a/backend/src/main/resources/db/migration/V0_0_14__add_member_view.sql b/backend/src/main/resources/db/migration/V0_0_14__add_member_view.sql index 1aa3fb9ce..5e893abf2 100644 --- a/backend/src/main/resources/db/migration/V0_0_14__add_member_view.sql +++ b/backend/src/main/resources/db/migration/V0_0_14__add_member_view.sql @@ -1,185 +1,173 @@ DROP VIEW IF EXISTS member_overview_view; CREATE VIEW member_overview_view AS -SELECT - m.id AS member_id, - m.first_name, - m.last_name, - m.abbreviation, - m.birth_date, - m.date_of_hire, - m.employment_state, - - ou.id AS organisation_unit_id, - ou.name AS organisation_unit_name, - - -- Certificate row (0..1 per certificate_type per row) - cert_row.certificate_id, - cert_row.certificate_completed_at, - cert_row.certificate_valid_until, - cert_row.certificate_comment, - cert_row.certificate_type_id, - cert_row.certificate_type_name, - cert_row.certificate_type_points, - cert_row.certificate_type_comment, - cert_row.certificate_kind, - cert_row.tag_id, - cert_row.tag_name, - - -- Leadership row - leader_row.leadership_id, - leader_row.leadership_comment, - leader_row.leadership_type_id, - leader_row.leadership_type_name, - leader_row.leadership_kind, - leader_row.leadership_type_points, - leader_row.leadership_type_comment, - - -- Degree row - degree_row.degree_id, - degree_row.degree_name, - degree_row.degree_start_date, - degree_row.degree_end_date, - degree_row.degree_institution, - degree_row.degree_completed, - degree_row.degree_comment, - degree_row.degree_type_id, - degree_row.degree_type_name, - degree_row.degree_highly_relevant_points, - degree_row.degree_limited_relevant_points, - degree_row.degree_little_relevant_points, - - -- Experience row - exp_row.experience_id, - exp_row.experience_name, - exp_row.experience_employer, - exp_row.experience_start_date, - exp_row.experience_end_date, - exp_row.experience_percent, - exp_row.experience_comment, - exp_row.experience_type_id, - exp_row.experience_type_name, - exp_row.experience_highly_relevant_points, - exp_row.experience_limited_relevant_points, - exp_row.experience_little_relevant_points, - - -- Calculation row - calc_row.calculation_id, - calc_row.calculation_state, - calc_row.calculation_publication_date, - calc_row.calculation_publicized_by, - calc_row.role_id, - calc_row.role_name, - calc_row.role_is_management - -FROM member m - LEFT JOIN organisation_unit ou - ON ou.id = m.organisation_unit - --- ------------- CERTIFICATES + TAGS ------------------- - LEFT JOIN LATERAL ( - SELECT - cert.id AS certificate_id, - cert.completed_at AS certificate_completed_at, - cert.valid_until AS certificate_valid_until, - cert.comment AS certificate_comment, - - ct.id AS certificate_type_id, - ct.name AS certificate_type_name, - ct.points AS certificate_type_points, - ct.comment AS certificate_type_comment, - ct.certificate_kind, - - t.id AS tag_id, - t.name AS tag_name - FROM certificate cert - JOIN certificate_type ct - ON ct.id = cert.certificate_type_id - LEFT JOIN certificate_type_tag ctt - ON ctt.certificate_type_id = ct.id - LEFT JOIN tag t - ON t.id = ctt.tag_id - WHERE cert.member_id = m.id - AND cert.deleted_at IS NULL - AND ct.certificate_kind = 'CERTIFICATE' - ) cert_row ON TRUE - --- ------------- LEADERSHIP EXPERIENCE ------------------- - LEFT JOIN LATERAL ( - SELECT - leader.id AS leadership_id, - leader.comment AS leadership_comment, - - lct.id AS leadership_type_id, - lct.name AS leadership_type_name, - lct.certificate_kind AS leadership_kind, - lct.points AS leadership_type_points, - lct.comment AS leadership_type_comment - FROM certificate leader - JOIN certificate_type lct - ON lct.id = leader.certificate_type_id - WHERE leader.member_id = m.id - AND leader.deleted_at IS NULL - AND lct.certificate_kind != 'CERTIFICATE' - ) leader_row ON TRUE - --- ------------- DEGREES ------------------- - LEFT JOIN LATERAL ( +WITH base_data AS ( SELECT - d.id AS degree_id, - d.name AS degree_name, - d.start_date AS degree_start_date, - d.end_date AS degree_end_date, - d.institution AS degree_institution, - d.completed AS degree_completed, - d.comment AS degree_comment, - - dt.id AS degree_type_id, - dt.name AS degree_type_name, - dt.highly_relevant_points AS degree_highly_relevant_points, - dt.limited_relevant_points AS degree_limited_relevant_points, - dt.little_relevant_points AS degree_little_relevant_points - FROM degree d - LEFT JOIN degree_type dt - ON dt.id = d.degree_type_id - WHERE d.member_id = m.id - ) degree_row ON TRUE - --- ------------- EXPERIENCES ------------------- - LEFT JOIN LATERAL ( - SELECT - e.id AS experience_id, - e.name AS experience_name, - e.employer AS experience_employer, - e.start_date AS experience_start_date, - e.end_date AS experience_end_date, - e.percent AS experience_percent, - e.comment AS experience_comment, - - et.id AS experience_type_id, - et.name AS experience_type_name, - et.highly_relevant_points AS experience_highly_relevant_points, - et.limited_relevant_points AS experience_limited_relevant_points, - et.little_relevant_points AS experience_little_relevant_points - FROM experience e - LEFT JOIN experience_type et - ON et.id = e.experience_type_id - WHERE e.member_id = m.id - ) exp_row ON TRUE - --- ------------- CALCULATIONS ------------------- - LEFT JOIN LATERAL ( - SELECT - ca.id AS calculation_id, - ca.state AS calculation_state, - ca.publication_date AS calculation_publication_date, - ca.publicized_by AS calculation_publicized_by, - - r.id AS role_id, - r.name AS role_name, - r.is_management AS role_is_management - FROM calculation ca - LEFT JOIN role r - ON r.id = ca.role_id - WHERE ca.member_id = m.id - ) calc_row ON TRUE; + m.id AS member_id, + m.first_name, + m.last_name, + m.abbreviation, + m.birth_date, + m.date_of_hire, + m.employment_state, + + ou.id AS organisation_unit_id, + ou.name AS organisation_unit_name, + + cert_row.certificate_id, + cert_row.certificate_completed_at, + cert_row.certificate_valid_until, + cert_row.certificate_comment, + cert_row.certificate_type_id, + cert_row.certificate_type_name, + cert_row.certificate_type_points, + cert_row.certificate_type_comment, + cert_row.certificate_kind, + cert_row.tag_id, + cert_row.tag_name, + + leader_row.leadership_id, + leader_row.leadership_comment, + leader_row.leadership_type_id, + leader_row.leadership_type_name, + leader_row.leadership_kind, + leader_row.leadership_type_points, + leader_row.leadership_type_comment, + + degree_row.degree_id, + degree_row.degree_name, + degree_row.degree_start_date, + degree_row.degree_end_date, + degree_row.degree_institution, + degree_row.degree_completed, + degree_row.degree_comment, + degree_row.degree_type_id, + degree_row.degree_type_name, + degree_row.degree_highly_relevant_points, + degree_row.degree_limited_relevant_points, + degree_row.degree_little_relevant_points, + + exp_row.experience_id, + exp_row.experience_name, + exp_row.experience_employer, + exp_row.experience_start_date, + exp_row.experience_end_date, + exp_row.experience_percent, + exp_row.experience_comment, + exp_row.experience_type_id, + exp_row.experience_type_name, + exp_row.experience_highly_relevant_points, + exp_row.experience_limited_relevant_points, + exp_row.experience_little_relevant_points, + + calc_row.calculation_id, + calc_row.calculation_state, + calc_row.calculation_publication_date, + calc_row.calculation_publicized_by, + calc_row.role_id, + calc_row.role_name, + calc_row.role_is_management + + FROM member m + LEFT JOIN organisation_unit ou ON ou.id = m.organisation_unit + + LEFT JOIN LATERAL ( + SELECT + cert.id AS certificate_id, + cert.completed_at AS certificate_completed_at, + cert.valid_until AS certificate_valid_until, + cert.comment AS certificate_comment, + + ct.id AS certificate_type_id, + ct.name AS certificate_type_name, + ct.points AS certificate_type_points, + ct.comment AS certificate_type_comment, + ct.certificate_kind, + + t.id AS tag_id, + t.name AS tag_name + FROM certificate cert + JOIN certificate_type ct ON ct.id = cert.certificate_type_id + LEFT JOIN certificate_type_tag ctt ON ctt.certificate_type_id = ct.id + LEFT JOIN tag t ON t.id = ctt.tag_id + WHERE cert.member_id = m.id + AND cert.deleted_at IS NULL + AND ct.certificate_kind = 'CERTIFICATE' + ) cert_row ON TRUE + + LEFT JOIN LATERAL ( + SELECT + leader.id AS leadership_id, + leader.comment AS leadership_comment, + + lct.id AS leadership_type_id, + lct.name AS leadership_type_name, + lct.certificate_kind AS leadership_kind, + lct.points AS leadership_type_points, + lct.comment AS leadership_type_comment + FROM certificate leader + JOIN certificate_type lct ON lct.id = leader.certificate_type_id + WHERE leader.member_id = m.id + AND leader.deleted_at IS NULL + AND lct.certificate_kind != 'CERTIFICATE' + ) leader_row ON TRUE + + LEFT JOIN LATERAL ( + SELECT + d.id AS degree_id, + d.name AS degree_name, + d.start_date AS degree_start_date, + d.end_date AS degree_end_date, + d.institution AS degree_institution, + d.completed AS degree_completed, + d.comment AS degree_comment, + + dt.id AS degree_type_id, + dt.name AS degree_type_name, + dt.highly_relevant_points AS degree_highly_relevant_points, + dt.limited_relevant_points AS degree_limited_relevant_points, + dt.little_relevant_points AS degree_little_relevant_points + FROM degree d + LEFT JOIN degree_type dt ON dt.id = d.degree_type_id + WHERE d.member_id = m.id + ) degree_row ON TRUE + + LEFT JOIN LATERAL ( + SELECT + e.id AS experience_id, + e.name AS experience_name, + e.employer AS experience_employer, + e.start_date AS experience_start_date, + e.end_date AS experience_end_date, + e.percent AS experience_percent, + e.comment AS experience_comment, + + et.id AS experience_type_id, + et.name AS experience_type_name, + et.highly_relevant_points AS experience_highly_relevant_points, + et.limited_relevant_points AS experience_limited_relevant_points, + et.little_relevant_points AS experience_little_relevant_points + FROM experience e + LEFT JOIN experience_type et ON et.id = e.experience_type_id + WHERE e.member_id = m.id + ) exp_row ON TRUE + + LEFT JOIN LATERAL ( + SELECT + ca.id AS calculation_id, + ca.state AS calculation_state, + ca.publication_date AS calculation_publication_date, + ca.publicized_by AS calculation_publicized_by, + + r.id AS role_id, + r.name AS role_name, + r.is_management AS role_is_management + FROM calculation ca + LEFT JOIN role r ON r.id = ca.role_id + WHERE ca.member_id = m.id + ) calc_row ON TRUE +) +SELECT + ROW_NUMBER() OVER (ORDER BY member_id) AS unique_row_id, + base_data.* +FROM base_data; From c21824e7a9d54dac58d368505dc2ba3b8fe9f634 Mon Sep 17 00:00:00 2001 From: Manuel Date: Mon, 15 Dec 2025 12:03:40 +0100 Subject: [PATCH 06/30] test(backend): Fix existing test by adding trim to every string #50 --- .../model/memberoverview/MemberOverview.java | 141 +++++++++--------- .../db/migration/V0_0_14__add_member_view.sql | 4 +- 2 files changed, 74 insertions(+), 71 deletions(-) diff --git a/backend/src/main/java/ch/puzzle/pcts/model/memberoverview/MemberOverview.java b/backend/src/main/java/ch/puzzle/pcts/model/memberoverview/MemberOverview.java index 7737465f1..7c013c019 100644 --- a/backend/src/main/java/ch/puzzle/pcts/model/memberoverview/MemberOverview.java +++ b/backend/src/main/java/ch/puzzle/pcts/model/memberoverview/MemberOverview.java @@ -1,5 +1,7 @@ package ch.puzzle.pcts.model.memberoverview; +import static org.apache.commons.lang3.StringUtils.trim; + import ch.puzzle.pcts.model.Model; import ch.puzzle.pcts.model.calculation.CalculationState; import ch.puzzle.pcts.model.certificatetype.CertificateKind; @@ -38,7 +40,8 @@ public class MemberOverview implements Model { private String certificateTypeName; private BigDecimal certificateTypePoints; private String certificateTypeComment; - private String certificateKind; + @Enumerated(EnumType.STRING) + private CertificateKind certificateKind; private Long tagId; private String tagName; @@ -94,62 +97,62 @@ public class MemberOverview implements Model { private MemberOverview(Builder builder) { uniqueRowId = builder.uniqueRowId; memberId = builder.memberId; - firstName = builder.firstName; - lastName = builder.lastName; - abbreviation = builder.abbreviation; + firstName = trim(builder.firstName); + lastName = trim(builder.lastName); + abbreviation = trim(builder.abbreviation); birthDate = builder.birthDate; dateOfHire = builder.dateOfHire; employmentState = builder.employmentState; organisationUnitId = builder.organisationUnitId; - organisationUnitName = builder.organisationUnitName; + organisationUnitName = trim(builder.organisationUnitName); certificateId = builder.certificateId; certificateCompletedAt = builder.certificateCompletedAt; certificateValidUntil = builder.certificateValidUntil; - certificateComment = builder.certificateComment; + certificateComment = trim(builder.certificateComment); certificateTypeId = builder.certificateTypeId; - certificateTypeName = builder.certificateTypeName; + certificateTypeName = trim(builder.certificateTypeName); certificateTypePoints = builder.certificateTypePoints; - certificateTypeComment = builder.certificateTypeComment; + certificateTypeComment = trim(builder.certificateTypeComment); certificateKind = builder.certificateKind; tagId = builder.tagId; - tagName = builder.tagName; + tagName = trim(builder.tagName); leadershipId = builder.leadershipId; - leadershipComment = builder.leadershipComment; + leadershipComment = trim(builder.leadershipComment); degreeId = builder.degreeId; - degreeName = builder.degreeName; + degreeName = trim(builder.degreeName); degreeStartDate = builder.degreeStartDate; degreeEndDate = builder.degreeEndDate; - degreeInstitution = builder.degreeInstitution; + degreeInstitution = trim(builder.degreeInstitution); degreeCompleted = builder.degreeCompleted; - degreeComment = builder.degreeComment; + degreeComment = trim(builder.degreeComment); leadershipTypeId = builder.leadershipTypeId; - leadershipTypeName = builder.leadershipTypeName; + leadershipTypeName = trim(builder.leadershipTypeName); leadershipKind = builder.leadershipKind; leadershipTypePoints = builder.leadershipTypePoints; - leadershipTypeComment = builder.leadershipTypeComment; + leadershipTypeComment = trim(builder.leadershipTypeComment); degreeTypeId = builder.degreeTypeId; - degreeTypeName = builder.degreeTypeName; + degreeTypeName = trim(builder.degreeTypeName); degreeHighlyRelevantPoints = builder.degreeHighlyRelevantPoints; degreeLimitedRelevantPoints = builder.degreeLimitedRelevantPoints; degreeLittleRelevantPoints = builder.degreeLittleRelevantPoints; experienceId = builder.experienceId; - experienceName = builder.experienceName; - experienceEmployer = builder.experienceEmployer; + experienceName = trim(builder.experienceName); + experienceEmployer = trim(builder.experienceEmployer); experienceStartDate = builder.experienceStartDate; experienceEndDate = builder.experienceEndDate; experiencePercent = builder.experiencePercent; - experienceComment = builder.experienceComment; + experienceComment = trim(builder.experienceComment); experienceTypeId = builder.experienceTypeId; - experienceTypeName = builder.experienceTypeName; + experienceTypeName = trim(builder.experienceTypeName); experienceHighlyRelevantPoints = builder.experienceHighlyRelevantPoints; experienceLimitedRelevantPoints = builder.experienceLimitedRelevantPoints; experienceLittleRelevantPoints = builder.experienceLittleRelevantPoints; calculationId = builder.calculationId; calculationState = builder.calculationState; calculationPublicationDate = builder.calculationPublicationDate; - calculationPublicizedBy = builder.calculationPublicizedBy; + calculationPublicizedBy = trim(builder.calculationPublicizedBy); roleId = builder.roleId; - roleName = builder.roleName; + roleName = trim(builder.roleName); roleIsManagement = builder.roleIsManagement; } @@ -180,7 +183,7 @@ public String getFirstName() { } public void setFirstName(String firstName) { - this.firstName = firstName; + this.firstName = trim(firstName); } public String getLastName() { @@ -188,7 +191,7 @@ public String getLastName() { } public void setLastName(String lastName) { - this.lastName = lastName; + this.lastName = trim(lastName); } public String getAbbreviation() { @@ -196,7 +199,7 @@ public String getAbbreviation() { } public void setAbbreviation(String abbreviation) { - this.abbreviation = abbreviation; + this.abbreviation = trim(abbreviation); } public LocalDate getBirthDate() { @@ -236,7 +239,7 @@ public String getOrganisationUnitName() { } public void setOrganisationUnitName(String organisationUnitName) { - this.organisationUnitName = organisationUnitName; + this.organisationUnitName = trim(organisationUnitName); } public Long getCertificateId() { @@ -268,7 +271,7 @@ public String getCertificateComment() { } public void setCertificateComment(String certificateComment) { - this.certificateComment = certificateComment; + this.certificateComment = trim(certificateComment); } public Long getCertificateTypeId() { @@ -284,7 +287,7 @@ public String getCertificateTypeName() { } public void setCertificateTypeName(String certificateTypeName) { - this.certificateTypeName = certificateTypeName; + this.certificateTypeName = trim(certificateTypeName); } public BigDecimal getCertificateTypePoints() { @@ -300,14 +303,14 @@ public String getCertificateTypeComment() { } public void setCertificateTypeComment(String certificateTypeComment) { - this.certificateTypeComment = certificateTypeComment; + this.certificateTypeComment = trim(certificateTypeComment); } - public String getCertificateKind() { + public CertificateKind getCertificateKind() { return certificateKind; } - public void setCertificateKind(String certificateKind) { + public void setCertificateKind(CertificateKind certificateKind) { this.certificateKind = certificateKind; } @@ -324,7 +327,7 @@ public String getTagName() { } public void setTagName(String tagName) { - this.tagName = tagName; + this.tagName = trim(tagName); } public Long getLeadershipId() { @@ -340,7 +343,7 @@ public String getLeadershipComment() { } public void setLeadershipComment(String leadershipComment) { - this.leadershipComment = leadershipComment; + this.leadershipComment = trim(leadershipComment); } public Long getDegreeId() { @@ -356,7 +359,7 @@ public String getDegreeName() { } public void setDegreeName(String degreeName) { - this.degreeName = degreeName; + this.degreeName = trim(degreeName); } public LocalDate getDegreeStartDate() { @@ -380,7 +383,7 @@ public String getDegreeInstitution() { } public void setDegreeInstitution(String degreeInstitution) { - this.degreeInstitution = degreeInstitution; + this.degreeInstitution = trim(degreeInstitution); } public Boolean getDegreeCompleted() { @@ -396,7 +399,7 @@ public String getDegreeComment() { } public void setDegreeComment(String degreeComment) { - this.degreeComment = degreeComment; + this.degreeComment = trim(degreeComment); } public Long getLeadershipTypeId() { @@ -412,7 +415,7 @@ public String getLeadershipTypeName() { } public void setLeadershipTypeName(String leadershipTypeName) { - this.leadershipTypeName = leadershipTypeName; + this.leadershipTypeName = trim(leadershipTypeName); } public CertificateKind getLeadershipKind() { @@ -436,7 +439,7 @@ public String getLeadershipTypeComment() { } public void setLeadershipTypeComment(String leadershipTypeComment) { - this.leadershipTypeComment = leadershipTypeComment; + this.leadershipTypeComment = trim(leadershipTypeComment); } public Long getDegreeTypeId() { @@ -452,7 +455,7 @@ public String getDegreeTypeName() { } public void setDegreeTypeName(String degreeTypeName) { - this.degreeTypeName = degreeTypeName; + this.degreeTypeName = trim(degreeTypeName); } public BigDecimal getDegreeHighlyRelevantPoints() { @@ -492,7 +495,7 @@ public String getExperienceName() { } public void setExperienceName(String experienceName) { - this.experienceName = experienceName; + this.experienceName = trim(experienceName); } public String getExperienceEmployer() { @@ -500,7 +503,7 @@ public String getExperienceEmployer() { } public void setExperienceEmployer(String experienceEmployer) { - this.experienceEmployer = experienceEmployer; + this.experienceEmployer = trim(experienceEmployer); } public LocalDate getExperienceStartDate() { @@ -532,7 +535,7 @@ public String getExperienceComment() { } public void setExperienceComment(String experienceComment) { - this.experienceComment = experienceComment; + this.experienceComment = trim(experienceComment); } public Long getExperienceTypeId() { @@ -548,7 +551,7 @@ public String getExperienceTypeName() { } public void setExperienceTypeName(String experienceTypeName) { - this.experienceTypeName = experienceTypeName; + this.experienceTypeName = trim(experienceTypeName); } public BigDecimal getExperienceHighlyRelevantPoints() { @@ -604,7 +607,7 @@ public String getCalculationPublicizedBy() { } public void setCalculationPublicizedBy(String calculationPublicizedBy) { - this.calculationPublicizedBy = calculationPublicizedBy; + this.calculationPublicizedBy = trim(calculationPublicizedBy); } public Long getRoleId() { @@ -620,7 +623,7 @@ public String getRoleName() { } public void setRoleName(String roleName) { - this.roleName = roleName; + this.roleName = trim(roleName); } public Boolean getRoleIsManagement() { @@ -668,7 +671,7 @@ public static final class Builder { private String certificateTypeName; private BigDecimal certificateTypePoints; private String certificateTypeComment; - private String certificateKind; + private CertificateKind certificateKind; private Long tagId; private String tagName; @@ -737,17 +740,17 @@ public Builder withMemberId(Long memberId) { } public Builder withFirstName(String firstName) { - this.firstName = firstName; + this.firstName = trim(firstName); return this; } public Builder withLastName(String lastName) { - this.lastName = lastName; + this.lastName = trim(lastName); return this; } public Builder withAbbreviation(String abbreviation) { - this.abbreviation = abbreviation; + this.abbreviation = trim(abbreviation); return this; } @@ -772,7 +775,7 @@ public Builder withOrganisationUnitId(Long organisationUnitId) { } public Builder withOrganisationUnitName(String organisationUnitName) { - this.organisationUnitName = organisationUnitName; + this.organisationUnitName = trim(organisationUnitName); return this; } @@ -792,7 +795,7 @@ public Builder withCertificateValidUntil(LocalDate certificateValidUntil) { } public Builder withCertificateComment(String certificateComment) { - this.certificateComment = certificateComment; + this.certificateComment = trim(certificateComment); return this; } @@ -802,7 +805,7 @@ public Builder withCertificateTypeId(Long certificateTypeId) { } public Builder withCertificateTypeName(String certificateTypeName) { - this.certificateTypeName = certificateTypeName; + this.certificateTypeName = trim(certificateTypeName); return this; } @@ -812,11 +815,11 @@ public Builder withCertificateTypePoints(BigDecimal certificateTypePoints) { } public Builder withCertificateTypeComment(String certificateTypeComment) { - this.certificateTypeComment = certificateTypeComment; + this.certificateTypeComment = trim(certificateTypeComment); return this; } - public Builder withCertificateKind(String certificateKind) { + public Builder withCertificateKind(CertificateKind certificateKind) { this.certificateKind = certificateKind; return this; } @@ -827,7 +830,7 @@ public Builder withTagId(Long tagId) { } public Builder withTagName(String tagName) { - this.tagName = tagName; + this.tagName = trim(tagName); return this; } @@ -837,7 +840,7 @@ public Builder withLeadershipId(Long leadershipId) { } public Builder withLeadershipComment(String leadershipComment) { - this.leadershipComment = leadershipComment; + this.leadershipComment = trim(leadershipComment); return this; } @@ -847,7 +850,7 @@ public Builder withLeadershipTypeId(Long leadershipTypeId) { } public Builder withLeadershipTypeName(String leadershipTypeName) { - this.leadershipTypeName = leadershipTypeName; + this.leadershipTypeName = trim(leadershipTypeName); return this; } @@ -862,7 +865,7 @@ public Builder withLeadershipTypePoints(BigDecimal leadershipTypePoints) { } public Builder withLeadershipTypeComment(String leadershipTypeComment) { - this.leadershipTypeComment = leadershipTypeComment; + this.leadershipTypeComment = trim(leadershipTypeComment); return this; } @@ -872,7 +875,7 @@ public Builder withDegreeId(Long degreeId) { } public Builder withDegreeName(String degreeName) { - this.degreeName = degreeName; + this.degreeName = trim(degreeName); return this; } @@ -887,7 +890,7 @@ public Builder withDegreeEndDate(LocalDate degreeEndDate) { } public Builder withDegreeInstitution(String degreeInstitution) { - this.degreeInstitution = degreeInstitution; + this.degreeInstitution = trim(degreeInstitution); return this; } @@ -897,7 +900,7 @@ public Builder withDegreeCompleted(Boolean degreeCompleted) { } public Builder withDegreeComment(String degreeComment) { - this.degreeComment = degreeComment; + this.degreeComment = trim(degreeComment); return this; } @@ -907,7 +910,7 @@ public Builder withDegreeTypeId(Long degreeTypeId) { } public Builder withDegreeTypeName(String degreeTypeName) { - this.degreeTypeName = degreeTypeName; + this.degreeTypeName = trim(degreeTypeName); return this; } @@ -932,12 +935,12 @@ public Builder withExperienceId(Long experienceId) { } public Builder withExperienceName(String experienceName) { - this.experienceName = experienceName; + this.experienceName = trim(experienceName); return this; } public Builder withExperienceEmployer(String experienceEmployer) { - this.experienceEmployer = experienceEmployer; + this.experienceEmployer = trim(experienceEmployer); return this; } @@ -957,7 +960,7 @@ public Builder withExperiencePercent(Integer experiencePercent) { } public Builder withExperienceComment(String experienceComment) { - this.experienceComment = experienceComment; + this.experienceComment = trim(experienceComment); return this; } @@ -967,7 +970,7 @@ public Builder withExperienceTypeId(Long experienceTypeId) { } public Builder withExperienceTypeName(String experienceTypeName) { - this.experienceTypeName = experienceTypeName; + this.experienceTypeName = trim(experienceTypeName); return this; } @@ -1002,7 +1005,7 @@ public Builder withCalculationPublicationDate(LocalDate calculationPublicationDa } public Builder withCalculationPublicizedBy(String calculationPublicizedBy) { - this.calculationPublicizedBy = calculationPublicizedBy; + this.calculationPublicizedBy = trim(calculationPublicizedBy); return this; } @@ -1012,7 +1015,7 @@ public Builder withRoleId(Long roleId) { } public Builder withRoleName(String roleName) { - this.roleName = roleName; + this.roleName = trim(roleName); return this; } @@ -1021,4 +1024,4 @@ public Builder withRoleIsManagement(Boolean roleIsManagement) { return this; } } -} +} \ No newline at end of file diff --git a/backend/src/main/resources/db/migration/V0_0_14__add_member_view.sql b/backend/src/main/resources/db/migration/V0_0_14__add_member_view.sql index 5e893abf2..2fad5b3ae 100644 --- a/backend/src/main/resources/db/migration/V0_0_14__add_member_view.sql +++ b/backend/src/main/resources/db/migration/V0_0_14__add_member_view.sql @@ -168,6 +168,6 @@ WITH base_data AS ( ) calc_row ON TRUE ) SELECT - ROW_NUMBER() OVER (ORDER BY member_id) AS unique_row_id, - base_data.* + ROW_NUMBER() OVER (ORDER BY member_id) AS unique_row_id, + base_data.* FROM base_data; From 80dc520865fb25539d42e7fa697f9dc456387b82 Mon Sep 17 00:00:00 2001 From: Manuel Date: Mon, 15 Dec 2025 15:02:56 +0100 Subject: [PATCH 07/30] refactor(MemberOverview): Remove unwanted attribute #50 --- .../pcts/mapper/MemberOverviewMapper.java | 2 +- .../model/memberoverview/MemberOverview.java | 17 ----------------- 2 files changed, 1 insertion(+), 18 deletions(-) diff --git a/backend/src/main/java/ch/puzzle/pcts/mapper/MemberOverviewMapper.java b/backend/src/main/java/ch/puzzle/pcts/mapper/MemberOverviewMapper.java index 61ffd9a9c..8c97e1f67 100644 --- a/backend/src/main/java/ch/puzzle/pcts/mapper/MemberOverviewMapper.java +++ b/backend/src/main/java/ch/puzzle/pcts/mapper/MemberOverviewMapper.java @@ -91,7 +91,7 @@ public MemberOverviewDto toDto(List entities) { Map> certificateGroups = entities .stream() - .filter(e -> e.getCertificateId() != null && "CERTIFICATE".equals(e.getCertificateKind())) + .filter(e -> e.getCertificateId() != null) .collect(Collectors.groupingBy(MemberOverview::getCertificateId)); List certificates = certificateGroups.values().stream().map(certRows -> { diff --git a/backend/src/main/java/ch/puzzle/pcts/model/memberoverview/MemberOverview.java b/backend/src/main/java/ch/puzzle/pcts/model/memberoverview/MemberOverview.java index 7c013c019..4e408a619 100644 --- a/backend/src/main/java/ch/puzzle/pcts/model/memberoverview/MemberOverview.java +++ b/backend/src/main/java/ch/puzzle/pcts/model/memberoverview/MemberOverview.java @@ -40,8 +40,6 @@ public class MemberOverview implements Model { private String certificateTypeName; private BigDecimal certificateTypePoints; private String certificateTypeComment; - @Enumerated(EnumType.STRING) - private CertificateKind certificateKind; private Long tagId; private String tagName; @@ -113,7 +111,6 @@ private MemberOverview(Builder builder) { certificateTypeName = trim(builder.certificateTypeName); certificateTypePoints = builder.certificateTypePoints; certificateTypeComment = trim(builder.certificateTypeComment); - certificateKind = builder.certificateKind; tagId = builder.tagId; tagName = trim(builder.tagName); leadershipId = builder.leadershipId; @@ -306,14 +303,6 @@ public void setCertificateTypeComment(String certificateTypeComment) { this.certificateTypeComment = trim(certificateTypeComment); } - public CertificateKind getCertificateKind() { - return certificateKind; - } - - public void setCertificateKind(CertificateKind certificateKind) { - this.certificateKind = certificateKind; - } - public Long getTagId() { return tagId; } @@ -671,7 +660,6 @@ public static final class Builder { private String certificateTypeName; private BigDecimal certificateTypePoints; private String certificateTypeComment; - private CertificateKind certificateKind; private Long tagId; private String tagName; @@ -819,11 +807,6 @@ public Builder withCertificateTypeComment(String certificateTypeComment) { return this; } - public Builder withCertificateKind(CertificateKind certificateKind) { - this.certificateKind = certificateKind; - return this; - } - public Builder withTagId(Long tagId) { this.tagId = tagId; return this; From b3d50200e2d0f07271383332b41707c4610d2046 Mon Sep 17 00:00:00 2001 From: Manuel Date: Tue, 16 Dec 2025 13:55:36 +0100 Subject: [PATCH 08/30] refactor(backend): Size down endpoint to only hold the necessary data #50 --- .../model/memberoverview/MemberOverview.java | 453 +----------------- .../db/migration/V0_0_14__add_member_view.sql | 223 ++------- 2 files changed, 65 insertions(+), 611 deletions(-) diff --git a/backend/src/main/java/ch/puzzle/pcts/model/memberoverview/MemberOverview.java b/backend/src/main/java/ch/puzzle/pcts/model/memberoverview/MemberOverview.java index 4e408a619..11744633e 100644 --- a/backend/src/main/java/ch/puzzle/pcts/model/memberoverview/MemberOverview.java +++ b/backend/src/main/java/ch/puzzle/pcts/model/memberoverview/MemberOverview.java @@ -3,11 +3,9 @@ import static org.apache.commons.lang3.StringUtils.trim; import ch.puzzle.pcts.model.Model; -import ch.puzzle.pcts.model.calculation.CalculationState; import ch.puzzle.pcts.model.certificatetype.CertificateKind; import ch.puzzle.pcts.model.member.EmploymentState; import jakarta.persistence.*; -import java.math.BigDecimal; import java.time.LocalDate; @Entity @@ -15,7 +13,6 @@ public class MemberOverview implements Model { @Id - @Column(name = "unique_row_id") private Long uniqueRowId; private Long memberId; @@ -27,8 +24,6 @@ public class MemberOverview implements Model { private LocalDate dateOfHire; @Enumerated(EnumType.STRING) private EmploymentState employmentState; - - private Long organisationUnitId; private String organisationUnitName; private Long certificateId; @@ -36,61 +31,32 @@ public class MemberOverview implements Model { private LocalDate certificateValidUntil; private String certificateComment; - private Long certificateTypeId; private String certificateTypeName; - private BigDecimal certificateTypePoints; private String certificateTypeComment; - private Long tagId; - private String tagName; - private Long leadershipId; private String leadershipComment; + private String leadershipTypeName; + private String leadershipTypeComment; + @Enumerated(EnumType.STRING) + private CertificateKind leadershipTypeKind; + private Long degreeId; private String degreeName; private LocalDate degreeStartDate; private LocalDate degreeEndDate; - private String degreeInstitution; - private Boolean degreeCompleted; - private String degreeComment; - private Long leadershipTypeId; - private String leadershipTypeName; - @Enumerated(EnumType.STRING) - private CertificateKind leadershipKind; - private BigDecimal leadershipTypePoints; - private String leadershipTypeComment; - - private Long degreeTypeId; private String degreeTypeName; - private BigDecimal degreeHighlyRelevantPoints; - private BigDecimal degreeLimitedRelevantPoints; - private BigDecimal degreeLittleRelevantPoints; private Long experienceId; private String experienceName; private String experienceEmployer; private LocalDate experienceStartDate; private LocalDate experienceEndDate; - private Integer experiencePercent; private String experienceComment; - private Long experienceTypeId; private String experienceTypeName; - private BigDecimal experienceHighlyRelevantPoints; - private BigDecimal experienceLimitedRelevantPoints; - private BigDecimal experienceLittleRelevantPoints; - - private Long calculationId; - @Enumerated(EnumType.STRING) - private CalculationState calculationState; - private LocalDate calculationPublicationDate; - private String calculationPublicizedBy; - - private Long roleId; - private String roleName; - private Boolean roleIsManagement; private MemberOverview(Builder builder) { uniqueRowId = builder.uniqueRowId; @@ -101,56 +67,30 @@ private MemberOverview(Builder builder) { birthDate = builder.birthDate; dateOfHire = builder.dateOfHire; employmentState = builder.employmentState; - organisationUnitId = builder.organisationUnitId; organisationUnitName = trim(builder.organisationUnitName); certificateId = builder.certificateId; certificateCompletedAt = builder.certificateCompletedAt; certificateValidUntil = builder.certificateValidUntil; certificateComment = trim(builder.certificateComment); - certificateTypeId = builder.certificateTypeId; certificateTypeName = trim(builder.certificateTypeName); - certificateTypePoints = builder.certificateTypePoints; certificateTypeComment = trim(builder.certificateTypeComment); - tagId = builder.tagId; - tagName = trim(builder.tagName); leadershipId = builder.leadershipId; leadershipComment = trim(builder.leadershipComment); degreeId = builder.degreeId; degreeName = trim(builder.degreeName); degreeStartDate = builder.degreeStartDate; degreeEndDate = builder.degreeEndDate; - degreeInstitution = trim(builder.degreeInstitution); - degreeCompleted = builder.degreeCompleted; - degreeComment = trim(builder.degreeComment); - leadershipTypeId = builder.leadershipTypeId; leadershipTypeName = trim(builder.leadershipTypeName); - leadershipKind = builder.leadershipKind; - leadershipTypePoints = builder.leadershipTypePoints; + leadershipTypeKind = builder.leadershipTypeKind; leadershipTypeComment = trim(builder.leadershipTypeComment); - degreeTypeId = builder.degreeTypeId; degreeTypeName = trim(builder.degreeTypeName); - degreeHighlyRelevantPoints = builder.degreeHighlyRelevantPoints; - degreeLimitedRelevantPoints = builder.degreeLimitedRelevantPoints; - degreeLittleRelevantPoints = builder.degreeLittleRelevantPoints; experienceId = builder.experienceId; experienceName = trim(builder.experienceName); experienceEmployer = trim(builder.experienceEmployer); experienceStartDate = builder.experienceStartDate; experienceEndDate = builder.experienceEndDate; - experiencePercent = builder.experiencePercent; experienceComment = trim(builder.experienceComment); - experienceTypeId = builder.experienceTypeId; experienceTypeName = trim(builder.experienceTypeName); - experienceHighlyRelevantPoints = builder.experienceHighlyRelevantPoints; - experienceLimitedRelevantPoints = builder.experienceLimitedRelevantPoints; - experienceLittleRelevantPoints = builder.experienceLittleRelevantPoints; - calculationId = builder.calculationId; - calculationState = builder.calculationState; - calculationPublicationDate = builder.calculationPublicationDate; - calculationPublicizedBy = trim(builder.calculationPublicizedBy); - roleId = builder.roleId; - roleName = trim(builder.roleName); - roleIsManagement = builder.roleIsManagement; } public MemberOverview() { @@ -223,14 +163,6 @@ public void setEmploymentState(EmploymentState employmentState) { this.employmentState = employmentState; } - public Long getOrganisationUnitId() { - return organisationUnitId; - } - - public void setOrganisationUnitId(Long organisationUnitId) { - this.organisationUnitId = organisationUnitId; - } - public String getOrganisationUnitName() { return organisationUnitName; } @@ -271,14 +203,6 @@ public void setCertificateComment(String certificateComment) { this.certificateComment = trim(certificateComment); } - public Long getCertificateTypeId() { - return certificateTypeId; - } - - public void setCertificateTypeId(Long certificateTypeId) { - this.certificateTypeId = certificateTypeId; - } - public String getCertificateTypeName() { return certificateTypeName; } @@ -287,14 +211,6 @@ public void setCertificateTypeName(String certificateTypeName) { this.certificateTypeName = trim(certificateTypeName); } - public BigDecimal getCertificateTypePoints() { - return certificateTypePoints; - } - - public void setCertificateTypePoints(BigDecimal certificateTypePoints) { - this.certificateTypePoints = certificateTypePoints; - } - public String getCertificateTypeComment() { return certificateTypeComment; } @@ -303,22 +219,6 @@ public void setCertificateTypeComment(String certificateTypeComment) { this.certificateTypeComment = trim(certificateTypeComment); } - public Long getTagId() { - return tagId; - } - - public void setTagId(Long tagId) { - this.tagId = tagId; - } - - public String getTagName() { - return tagName; - } - - public void setTagName(String tagName) { - this.tagName = trim(tagName); - } - public Long getLeadershipId() { return leadershipId; } @@ -367,38 +267,6 @@ public void setDegreeEndDate(LocalDate degreeEndDate) { this.degreeEndDate = degreeEndDate; } - public String getDegreeInstitution() { - return degreeInstitution; - } - - public void setDegreeInstitution(String degreeInstitution) { - this.degreeInstitution = trim(degreeInstitution); - } - - public Boolean getDegreeCompleted() { - return degreeCompleted; - } - - public void setDegreeCompleted(Boolean degreeCompleted) { - this.degreeCompleted = degreeCompleted; - } - - public String getDegreeComment() { - return degreeComment; - } - - public void setDegreeComment(String degreeComment) { - this.degreeComment = trim(degreeComment); - } - - public Long getLeadershipTypeId() { - return leadershipTypeId; - } - - public void setLeadershipTypeId(Long leadershipTypeId) { - this.leadershipTypeId = leadershipTypeId; - } - public String getLeadershipTypeName() { return leadershipTypeName; } @@ -407,20 +275,12 @@ public void setLeadershipTypeName(String leadershipTypeName) { this.leadershipTypeName = trim(leadershipTypeName); } - public CertificateKind getLeadershipKind() { - return leadershipKind; + public CertificateKind getleadershipTypeKind() { + return leadershipTypeKind; } - public void setLeadershipKind(CertificateKind leadershipKind) { - this.leadershipKind = leadershipKind; - } - - public BigDecimal getLeadershipTypePoints() { - return leadershipTypePoints; - } - - public void setLeadershipTypePoints(BigDecimal leadershipTypePoints) { - this.leadershipTypePoints = leadershipTypePoints; + public void setleadershipTypeKind(CertificateKind leadershipKind) { + this.leadershipTypeKind = leadershipKind; } public String getLeadershipTypeComment() { @@ -431,14 +291,6 @@ public void setLeadershipTypeComment(String leadershipTypeComment) { this.leadershipTypeComment = trim(leadershipTypeComment); } - public Long getDegreeTypeId() { - return degreeTypeId; - } - - public void setDegreeTypeId(Long degreeTypeId) { - this.degreeTypeId = degreeTypeId; - } - public String getDegreeTypeName() { return degreeTypeName; } @@ -447,30 +299,6 @@ public void setDegreeTypeName(String degreeTypeName) { this.degreeTypeName = trim(degreeTypeName); } - public BigDecimal getDegreeHighlyRelevantPoints() { - return degreeHighlyRelevantPoints; - } - - public void setDegreeHighlyRelevantPoints(BigDecimal degreeHighlyRelevantPoints) { - this.degreeHighlyRelevantPoints = degreeHighlyRelevantPoints; - } - - public BigDecimal getDegreeLimitedRelevantPoints() { - return degreeLimitedRelevantPoints; - } - - public void setDegreeLimitedRelevantPoints(BigDecimal degreeLimitedRelevantPoints) { - this.degreeLimitedRelevantPoints = degreeLimitedRelevantPoints; - } - - public BigDecimal getDegreeLittleRelevantPoints() { - return degreeLittleRelevantPoints; - } - - public void setDegreeLittleRelevantPoints(BigDecimal degreeLittleRelevantPoints) { - this.degreeLittleRelevantPoints = degreeLittleRelevantPoints; - } - public Long getExperienceId() { return experienceId; } @@ -511,14 +339,6 @@ public void setExperienceEndDate(LocalDate experienceEndDate) { this.experienceEndDate = experienceEndDate; } - public Integer getExperiencePercent() { - return experiencePercent; - } - - public void setExperiencePercent(Integer experiencePercent) { - this.experiencePercent = experiencePercent; - } - public String getExperienceComment() { return experienceComment; } @@ -527,14 +347,6 @@ public void setExperienceComment(String experienceComment) { this.experienceComment = trim(experienceComment); } - public Long getExperienceTypeId() { - return experienceTypeId; - } - - public void setExperienceTypeId(Long experienceTypeId) { - this.experienceTypeId = experienceTypeId; - } - public String getExperienceTypeName() { return experienceTypeName; } @@ -543,86 +355,6 @@ public void setExperienceTypeName(String experienceTypeName) { this.experienceTypeName = trim(experienceTypeName); } - public BigDecimal getExperienceHighlyRelevantPoints() { - return experienceHighlyRelevantPoints; - } - - public void setExperienceHighlyRelevantPoints(BigDecimal experienceHighlyRelevantPoints) { - this.experienceHighlyRelevantPoints = experienceHighlyRelevantPoints; - } - - public BigDecimal getExperienceLimitedRelevantPoints() { - return experienceLimitedRelevantPoints; - } - - public void setExperienceLimitedRelevantPoints(BigDecimal experienceLimitedRelevantPoints) { - this.experienceLimitedRelevantPoints = experienceLimitedRelevantPoints; - } - - public BigDecimal getExperienceLittleRelevantPoints() { - return experienceLittleRelevantPoints; - } - - public void setExperienceLittleRelevantPoints(BigDecimal experienceLittleRelevantPoints) { - this.experienceLittleRelevantPoints = experienceLittleRelevantPoints; - } - - public Long getCalculationId() { - return calculationId; - } - - public void setCalculationId(Long calculationId) { - this.calculationId = calculationId; - } - - public CalculationState getCalculationState() { - return calculationState; - } - - public void setCalculationState(CalculationState calculationState) { - this.calculationState = calculationState; - } - - public LocalDate getCalculationPublicationDate() { - return calculationPublicationDate; - } - - public void setCalculationPublicationDate(LocalDate calculationPublicationDate) { - this.calculationPublicationDate = calculationPublicationDate; - } - - public String getCalculationPublicizedBy() { - return calculationPublicizedBy; - } - - public void setCalculationPublicizedBy(String calculationPublicizedBy) { - this.calculationPublicizedBy = trim(calculationPublicizedBy); - } - - public Long getRoleId() { - return roleId; - } - - public void setRoleId(Long roleId) { - this.roleId = roleId; - } - - public String getRoleName() { - return roleName; - } - - public void setRoleName(String roleName) { - this.roleName = trim(roleName); - } - - public Boolean getRoleIsManagement() { - return roleIsManagement; - } - - public void setRoleIsManagement(Boolean roleIsManagement) { - this.roleIsManagement = roleIsManagement; - } - @Override public int hashCode() { return super.hashCode(); @@ -648,7 +380,6 @@ public static final class Builder { private LocalDate dateOfHire; private EmploymentState employmentState; - private Long organisationUnitId; private String organisationUnitName; private Long certificateId; @@ -656,14 +387,9 @@ public static final class Builder { private LocalDate certificateValidUntil; private String certificateComment; - private Long certificateTypeId; private String certificateTypeName; - private BigDecimal certificateTypePoints; private String certificateTypeComment; - private Long tagId; - private String tagName; - private Long leadershipId; private String leadershipComment; @@ -671,44 +397,21 @@ public static final class Builder { private String degreeName; private LocalDate degreeStartDate; private LocalDate degreeEndDate; - private String degreeInstitution; - private Boolean degreeCompleted; - private String degreeComment; - private Long leadershipTypeId; private String leadershipTypeName; - private CertificateKind leadershipKind; - private BigDecimal leadershipTypePoints; + private CertificateKind leadershipTypeKind; private String leadershipTypeComment; - private Long degreeTypeId; private String degreeTypeName; - private BigDecimal degreeHighlyRelevantPoints; - private BigDecimal degreeLimitedRelevantPoints; - private BigDecimal degreeLittleRelevantPoints; private Long experienceId; private String experienceName; private String experienceEmployer; private LocalDate experienceStartDate; private LocalDate experienceEndDate; - private Integer experiencePercent; private String experienceComment; - private Long experienceTypeId; private String experienceTypeName; - private BigDecimal experienceHighlyRelevantPoints; - private BigDecimal experienceLimitedRelevantPoints; - private BigDecimal experienceLittleRelevantPoints; - - private Long calculationId; - private CalculationState calculationState; - private LocalDate calculationPublicationDate; - private String calculationPublicizedBy; - - private Long roleId; - private String roleName; - private Boolean roleIsManagement; private Builder() { } @@ -757,11 +460,6 @@ public Builder withEmploymentState(EmploymentState employmentState) { return this; } - public Builder withOrganisationUnitId(Long organisationUnitId) { - this.organisationUnitId = organisationUnitId; - return this; - } - public Builder withOrganisationUnitName(String organisationUnitName) { this.organisationUnitName = trim(organisationUnitName); return this; @@ -787,36 +485,16 @@ public Builder withCertificateComment(String certificateComment) { return this; } - public Builder withCertificateTypeId(Long certificateTypeId) { - this.certificateTypeId = certificateTypeId; - return this; - } - public Builder withCertificateTypeName(String certificateTypeName) { this.certificateTypeName = trim(certificateTypeName); return this; } - public Builder withCertificateTypePoints(BigDecimal certificateTypePoints) { - this.certificateTypePoints = certificateTypePoints; - return this; - } - public Builder withCertificateTypeComment(String certificateTypeComment) { this.certificateTypeComment = trim(certificateTypeComment); return this; } - public Builder withTagId(Long tagId) { - this.tagId = tagId; - return this; - } - - public Builder withTagName(String tagName) { - this.tagName = trim(tagName); - return this; - } - public Builder withLeadershipId(Long leadershipId) { this.leadershipId = leadershipId; return this; @@ -827,23 +505,13 @@ public Builder withLeadershipComment(String leadershipComment) { return this; } - public Builder withLeadershipTypeId(Long leadershipTypeId) { - this.leadershipTypeId = leadershipTypeId; - return this; - } - public Builder withLeadershipTypeName(String leadershipTypeName) { this.leadershipTypeName = trim(leadershipTypeName); return this; } - public Builder withLeadershipKind(CertificateKind leadershipKind) { - this.leadershipKind = leadershipKind; - return this; - } - - public Builder withLeadershipTypePoints(BigDecimal leadershipTypePoints) { - this.leadershipTypePoints = leadershipTypePoints; + public Builder withleadershipTypeKind(CertificateKind leadershipTypeKind) { + this.leadershipTypeKind = leadershipTypeKind; return this; } @@ -872,46 +540,11 @@ public Builder withDegreeEndDate(LocalDate degreeEndDate) { return this; } - public Builder withDegreeInstitution(String degreeInstitution) { - this.degreeInstitution = trim(degreeInstitution); - return this; - } - - public Builder withDegreeCompleted(Boolean degreeCompleted) { - this.degreeCompleted = degreeCompleted; - return this; - } - - public Builder withDegreeComment(String degreeComment) { - this.degreeComment = trim(degreeComment); - return this; - } - - public Builder withDegreeTypeId(Long degreeTypeId) { - this.degreeTypeId = degreeTypeId; - return this; - } - public Builder withDegreeTypeName(String degreeTypeName) { this.degreeTypeName = trim(degreeTypeName); return this; } - public Builder withDegreeHighlyRelevantPoints(BigDecimal degreeHighlyRelevantPoints) { - this.degreeHighlyRelevantPoints = degreeHighlyRelevantPoints; - return this; - } - - public Builder withDegreeLimitedRelevantPoints(BigDecimal degreeLimitedRelevantPoints) { - this.degreeLimitedRelevantPoints = degreeLimitedRelevantPoints; - return this; - } - - public Builder withDegreeLittleRelevantPoints(BigDecimal degreeLittleRelevantPoints) { - this.degreeLittleRelevantPoints = degreeLittleRelevantPoints; - return this; - } - public Builder withExperienceId(Long experienceId) { this.experienceId = experienceId; return this; @@ -937,74 +570,14 @@ public Builder withExperienceEndDate(LocalDate experienceEndDate) { return this; } - public Builder withExperiencePercent(Integer experiencePercent) { - this.experiencePercent = experiencePercent; - return this; - } - public Builder withExperienceComment(String experienceComment) { this.experienceComment = trim(experienceComment); return this; } - public Builder withExperienceTypeId(Long experienceTypeId) { - this.experienceTypeId = experienceTypeId; - return this; - } - public Builder withExperienceTypeName(String experienceTypeName) { this.experienceTypeName = trim(experienceTypeName); return this; } - - public Builder withExperienceHighlyRelevantPoints(BigDecimal experienceHighlyRelevantPoints) { - this.experienceHighlyRelevantPoints = experienceHighlyRelevantPoints; - return this; - } - - public Builder withExperienceLimitedRelevantPoints(BigDecimal experienceLimitedRelevantPoints) { - this.experienceLimitedRelevantPoints = experienceLimitedRelevantPoints; - return this; - } - - public Builder withExperienceLittleRelevantPoints(BigDecimal experienceLittleRelevantPoints) { - this.experienceLittleRelevantPoints = experienceLittleRelevantPoints; - return this; - } - - public Builder withCalculationId(Long calculationId) { - this.calculationId = calculationId; - return this; - } - - public Builder withCalculationState(CalculationState calculationState) { - this.calculationState = calculationState; - return this; - } - - public Builder withCalculationPublicationDate(LocalDate calculationPublicationDate) { - this.calculationPublicationDate = calculationPublicationDate; - return this; - } - - public Builder withCalculationPublicizedBy(String calculationPublicizedBy) { - this.calculationPublicizedBy = trim(calculationPublicizedBy); - return this; - } - - public Builder withRoleId(Long roleId) { - this.roleId = roleId; - return this; - } - - public Builder withRoleName(String roleName) { - this.roleName = trim(roleName); - return this; - } - - public Builder withRoleIsManagement(Boolean roleIsManagement) { - this.roleIsManagement = roleIsManagement; - return this; - } } } \ No newline at end of file diff --git a/backend/src/main/resources/db/migration/V0_0_14__add_member_view.sql b/backend/src/main/resources/db/migration/V0_0_14__add_member_view.sql index 2fad5b3ae..c37f91407 100644 --- a/backend/src/main/resources/db/migration/V0_0_14__add_member_view.sql +++ b/backend/src/main/resources/db/migration/V0_0_14__add_member_view.sql @@ -1,173 +1,54 @@ -DROP VIEW IF EXISTS member_overview_view; +DROP VIEW IF EXISTS member_overview; -CREATE VIEW member_overview_view AS -WITH base_data AS ( - SELECT - m.id AS member_id, - m.first_name, - m.last_name, - m.abbreviation, - m.birth_date, - m.date_of_hire, - m.employment_state, - - ou.id AS organisation_unit_id, - ou.name AS organisation_unit_name, - - cert_row.certificate_id, - cert_row.certificate_completed_at, - cert_row.certificate_valid_until, - cert_row.certificate_comment, - cert_row.certificate_type_id, - cert_row.certificate_type_name, - cert_row.certificate_type_points, - cert_row.certificate_type_comment, - cert_row.certificate_kind, - cert_row.tag_id, - cert_row.tag_name, - - leader_row.leadership_id, - leader_row.leadership_comment, - leader_row.leadership_type_id, - leader_row.leadership_type_name, - leader_row.leadership_kind, - leader_row.leadership_type_points, - leader_row.leadership_type_comment, - - degree_row.degree_id, - degree_row.degree_name, - degree_row.degree_start_date, - degree_row.degree_end_date, - degree_row.degree_institution, - degree_row.degree_completed, - degree_row.degree_comment, - degree_row.degree_type_id, - degree_row.degree_type_name, - degree_row.degree_highly_relevant_points, - degree_row.degree_limited_relevant_points, - degree_row.degree_little_relevant_points, - - exp_row.experience_id, - exp_row.experience_name, - exp_row.experience_employer, - exp_row.experience_start_date, - exp_row.experience_end_date, - exp_row.experience_percent, - exp_row.experience_comment, - exp_row.experience_type_id, - exp_row.experience_type_name, - exp_row.experience_highly_relevant_points, - exp_row.experience_limited_relevant_points, - exp_row.experience_little_relevant_points, - - calc_row.calculation_id, - calc_row.calculation_state, - calc_row.calculation_publication_date, - calc_row.calculation_publicized_by, - calc_row.role_id, - calc_row.role_name, - calc_row.role_is_management - - FROM member m - LEFT JOIN organisation_unit ou ON ou.id = m.organisation_unit - - LEFT JOIN LATERAL ( - SELECT - cert.id AS certificate_id, - cert.completed_at AS certificate_completed_at, - cert.valid_until AS certificate_valid_until, - cert.comment AS certificate_comment, - - ct.id AS certificate_type_id, - ct.name AS certificate_type_name, - ct.points AS certificate_type_points, - ct.comment AS certificate_type_comment, - ct.certificate_kind, - - t.id AS tag_id, - t.name AS tag_name - FROM certificate cert - JOIN certificate_type ct ON ct.id = cert.certificate_type_id - LEFT JOIN certificate_type_tag ctt ON ctt.certificate_type_id = ct.id - LEFT JOIN tag t ON t.id = ctt.tag_id - WHERE cert.member_id = m.id - AND cert.deleted_at IS NULL - AND ct.certificate_kind = 'CERTIFICATE' - ) cert_row ON TRUE - - LEFT JOIN LATERAL ( - SELECT - leader.id AS leadership_id, - leader.comment AS leadership_comment, - - lct.id AS leadership_type_id, - lct.name AS leadership_type_name, - lct.certificate_kind AS leadership_kind, - lct.points AS leadership_type_points, - lct.comment AS leadership_type_comment - FROM certificate leader - JOIN certificate_type lct ON lct.id = leader.certificate_type_id - WHERE leader.member_id = m.id - AND leader.deleted_at IS NULL - AND lct.certificate_kind != 'CERTIFICATE' - ) leader_row ON TRUE - - LEFT JOIN LATERAL ( - SELECT - d.id AS degree_id, - d.name AS degree_name, - d.start_date AS degree_start_date, - d.end_date AS degree_end_date, - d.institution AS degree_institution, - d.completed AS degree_completed, - d.comment AS degree_comment, - - dt.id AS degree_type_id, - dt.name AS degree_type_name, - dt.highly_relevant_points AS degree_highly_relevant_points, - dt.limited_relevant_points AS degree_limited_relevant_points, - dt.little_relevant_points AS degree_little_relevant_points - FROM degree d - LEFT JOIN degree_type dt ON dt.id = d.degree_type_id - WHERE d.member_id = m.id - ) degree_row ON TRUE - - LEFT JOIN LATERAL ( - SELECT - e.id AS experience_id, - e.name AS experience_name, - e.employer AS experience_employer, - e.start_date AS experience_start_date, - e.end_date AS experience_end_date, - e.percent AS experience_percent, - e.comment AS experience_comment, - - et.id AS experience_type_id, - et.name AS experience_type_name, - et.highly_relevant_points AS experience_highly_relevant_points, - et.limited_relevant_points AS experience_limited_relevant_points, - et.little_relevant_points AS experience_little_relevant_points - FROM experience e - LEFT JOIN experience_type et ON et.id = e.experience_type_id - WHERE e.member_id = m.id - ) exp_row ON TRUE - - LEFT JOIN LATERAL ( - SELECT - ca.id AS calculation_id, - ca.state AS calculation_state, - ca.publication_date AS calculation_publication_date, - ca.publicized_by AS calculation_publicized_by, - - r.id AS role_id, - r.name AS role_name, - r.is_management AS role_is_management - FROM calculation ca - LEFT JOIN role r ON r.id = ca.role_id - WHERE ca.member_id = m.id - ) calc_row ON TRUE -) +CREATE VIEW member_overview AS SELECT - ROW_NUMBER() OVER (ORDER BY member_id) AS unique_row_id, - base_data.* -FROM base_data; + m.id AS member_id, + m.first_name AS member_first_name, + m.last_name AS member_last_name, + m.abbreviation AS member_abbreviation, + m.employment_state AS member_employment_state, + m.date_of_hire AS member_date_of_hire, + m.birth_date AS member_birth_date, + ou.name AS organisation_unit_name, + + c.id AS certificate_id, + c.completed_at AS certificate_completed_at, + c.valid_until AS certificate_valid_until, + c.comment AS certificate_comment, + + ct.name AS certificate_type_name, + ct.comment AS certificate_type_comment, + + l.id AS leadership_id, + l.comment AS leadership_comment, + + lt.name AS leadership_type_name, + lt.comment AS leadership_type_comment, + lt.certificate_kind AS leadership_type_kind, + + d.id AS degree_id, + d.name AS degree_name, + d.start_date AS degree_start_date, + d.end_date AS degree_end_date, + + dt.name AS degree_type_name, + + e.id AS experience_id, + e.name AS experience_name, + e.employer AS experience_employer, + e.start_date AS experience_start_date, + e.end_date AS experience_end_date, + e.comment AS experience_comment, + + et.name AS experience_type_name + +FROM member m + LEFT JOIN organisation_unit ou ON ou.id = m.organisation_unit + LEFT JOIN certificate c ON c.member_id = m.id + LEFT JOIN certificate_type ct ON ct.id = c.certificate_type_id + LEFT JOIN certificate l ON l.member_id = m.id + LEFT JOIN certificate_type lt ON lt.id = l.certificate_type_id + LEFT JOIN degree d ON d.member_id = m.id + LEFT JOIN degree_type dt ON dt.id = d.degree_type_id + LEFT JOIN experience e ON e.member_id = m.id + LEFT JOIN experience_type et ON et.id = e.experience_type_id From f1d08267377df3e775395ab144a48678acf4b94e Mon Sep 17 00:00:00 2001 From: Manuel Date: Fri, 19 Dec 2025 15:18:18 +0100 Subject: [PATCH 09/30] refactor(dto): Add overview dtos for only the needed attributes #50 --- .../pcts/dto/memberoverview/MemberCvDto.java | 13 +-- .../dto/memberoverview/MemberOverviewDto.java | 6 +- .../MemberOverviewMemberDto.java | 16 +++ .../MemberOverviewCertificateDto.java | 12 +++ .../MemberOverviewCertificateTypeDto.java | 8 ++ .../degree/MemberOverviewDegreeDto.java | 18 ++++ .../degree/MemberOverviewDegreeTypeDto.java | 7 ++ .../MemberOverviewExperienceDto.java | 14 +++ .../MemberOverviewExperienceTypeDto.java | 7 ++ ...MemberOverviewLeadershipExperienceDto.java | 11 ++ ...erOverviewLeadershipExperienceTypeDto.java | 10 ++ .../model/memberoverview/MemberOverview.java | 87 ++++----------- .../db/migration/V0_0_14__add_member_view.sql | 100 +++++++++--------- 13 files changed, 180 insertions(+), 129 deletions(-) create mode 100644 backend/src/main/java/ch/puzzle/pcts/dto/memberoverview/MemberOverviewMemberDto.java create mode 100644 backend/src/main/java/ch/puzzle/pcts/dto/memberoverview/certificate/MemberOverviewCertificateDto.java create mode 100644 backend/src/main/java/ch/puzzle/pcts/dto/memberoverview/certificate/MemberOverviewCertificateTypeDto.java create mode 100644 backend/src/main/java/ch/puzzle/pcts/dto/memberoverview/degree/MemberOverviewDegreeDto.java create mode 100644 backend/src/main/java/ch/puzzle/pcts/dto/memberoverview/degree/MemberOverviewDegreeTypeDto.java create mode 100644 backend/src/main/java/ch/puzzle/pcts/dto/memberoverview/experience/MemberOverviewExperienceDto.java create mode 100644 backend/src/main/java/ch/puzzle/pcts/dto/memberoverview/experience/MemberOverviewExperienceTypeDto.java create mode 100644 backend/src/main/java/ch/puzzle/pcts/dto/memberoverview/leadershipexperience/MemberOverviewLeadershipExperienceDto.java create mode 100644 backend/src/main/java/ch/puzzle/pcts/dto/memberoverview/leadershipexperience/MemberOverviewLeadershipExperienceTypeDto.java diff --git a/backend/src/main/java/ch/puzzle/pcts/dto/memberoverview/MemberCvDto.java b/backend/src/main/java/ch/puzzle/pcts/dto/memberoverview/MemberCvDto.java index 3713605e6..ed66d5f5b 100644 --- a/backend/src/main/java/ch/puzzle/pcts/dto/memberoverview/MemberCvDto.java +++ b/backend/src/main/java/ch/puzzle/pcts/dto/memberoverview/MemberCvDto.java @@ -1,11 +1,12 @@ package ch.puzzle.pcts.dto.memberoverview; -import ch.puzzle.pcts.dto.certificate.CertificateDto; -import ch.puzzle.pcts.dto.degree.DegreeDto; -import ch.puzzle.pcts.dto.experience.ExperienceDto; -import ch.puzzle.pcts.dto.leadershipexperience.LeadershipExperienceDto; +import ch.puzzle.pcts.dto.memberoverview.certificate.MemberOverviewCertificateDto; +import ch.puzzle.pcts.dto.memberoverview.degree.MemberOverviewDegreeDto; +import ch.puzzle.pcts.dto.memberoverview.experience.MemberOverviewExperienceDto; +import ch.puzzle.pcts.dto.memberoverview.leadershipexperience.MemberOverviewLeadershipExperienceDto; import java.util.List; -public record MemberCvDto(List degrees, List experiences, List certificates, - List leadershipExperiences) { +public record MemberCvDto(List degrees, List experiences, + List certificates, + List leadershipExperiences) { } diff --git a/backend/src/main/java/ch/puzzle/pcts/dto/memberoverview/MemberOverviewDto.java b/backend/src/main/java/ch/puzzle/pcts/dto/memberoverview/MemberOverviewDto.java index 243287785..3929d35c7 100644 --- a/backend/src/main/java/ch/puzzle/pcts/dto/memberoverview/MemberOverviewDto.java +++ b/backend/src/main/java/ch/puzzle/pcts/dto/memberoverview/MemberOverviewDto.java @@ -1,8 +1,4 @@ package ch.puzzle.pcts.dto.memberoverview; -import ch.puzzle.pcts.dto.calculation.CalculationDto; -import ch.puzzle.pcts.dto.member.MemberDto; -import java.util.List; - -public record MemberOverviewDto(MemberDto member, MemberCvDto cv, List calculations) { +public record MemberOverviewDto(MemberOverviewMemberDto member, MemberCvDto cv) { } diff --git a/backend/src/main/java/ch/puzzle/pcts/dto/memberoverview/MemberOverviewMemberDto.java b/backend/src/main/java/ch/puzzle/pcts/dto/memberoverview/MemberOverviewMemberDto.java new file mode 100644 index 000000000..3ec4d7448 --- /dev/null +++ b/backend/src/main/java/ch/puzzle/pcts/dto/memberoverview/MemberOverviewMemberDto.java @@ -0,0 +1,16 @@ +package ch.puzzle.pcts.dto.memberoverview; + +import ch.puzzle.pcts.model.member.EmploymentState; +import io.swagger.v3.oas.annotations.media.Schema; +import java.time.LocalDate; + +public record MemberOverviewMemberDto( + @Schema(description = "The unique identifier of the member.", example = "1", requiredMode = Schema.RequiredMode.REQUIRED, accessMode = Schema.AccessMode.READ_ONLY) Long id, + @Schema(description = "The first name of member.", example = "Susi", requiredMode = Schema.RequiredMode.REQUIRED, minLength = 1) String firstName, + @Schema(description = "The last name of the member.", example = "Miller", requiredMode = Schema.RequiredMode.REQUIRED, minLength = 1) String lastName, + @Schema(description = "The employment state of the member.", example = "APPLICANT", requiredMode = Schema.RequiredMode.REQUIRED, minLength = 1) EmploymentState employmentState, + @Schema(description = "The abbreviation of the member.", example = "SM", requiredMode = Schema.RequiredMode.NOT_REQUIRED) String abbreviation, + @Schema(description = "The member's hire date.", example = "2025-09-24", requiredMode = Schema.RequiredMode.NOT_REQUIRED) LocalDate dateOfHire, + @Schema(description = "The member's birth date.", example = "1995-02-19", requiredMode = Schema.RequiredMode.REQUIRED) LocalDate birthDate, + @Schema(description = "The organisation unit name of the member.", example = "/dev", requiredMode = Schema.RequiredMode.NOT_REQUIRED) String organisationUnitName) { +} \ No newline at end of file diff --git a/backend/src/main/java/ch/puzzle/pcts/dto/memberoverview/certificate/MemberOverviewCertificateDto.java b/backend/src/main/java/ch/puzzle/pcts/dto/memberoverview/certificate/MemberOverviewCertificateDto.java new file mode 100644 index 000000000..a48aa7842 --- /dev/null +++ b/backend/src/main/java/ch/puzzle/pcts/dto/memberoverview/certificate/MemberOverviewCertificateDto.java @@ -0,0 +1,12 @@ +package ch.puzzle.pcts.dto.memberoverview.certificate; + +import io.swagger.v3.oas.annotations.media.Schema; +import java.time.LocalDate; + +public record MemberOverviewCertificateDto( + @Schema(description = "The unique identifier of the member certificate.", example = "1", requiredMode = Schema.RequiredMode.REQUIRED, accessMode = Schema.AccessMode.READ_ONLY, nullable = false) Long id, + @Schema(description = "The type of certificate awarded to the member.", exampleClasses = MemberOverviewCertificateTypeDto.class, requiredMode = Schema.RequiredMode.REQUIRED, nullable = false) MemberOverviewCertificateTypeDto certificate, + @Schema(description = "The date when the member completed the certificate.", example = "2025-09-24", requiredMode = Schema.RequiredMode.NOT_REQUIRED, nullable = true) LocalDate completedAt, + @Schema(description = "The date until which the certificate is valid.", example = "2028-02-12", requiredMode = Schema.RequiredMode.NOT_REQUIRED, nullable = true) LocalDate validUntil, + @Schema(description = "An optional comment for the member certificate.", example = "Completed via fast-track program", requiredMode = Schema.RequiredMode.NOT_REQUIRED, nullable = true) String comment) { +} diff --git a/backend/src/main/java/ch/puzzle/pcts/dto/memberoverview/certificate/MemberOverviewCertificateTypeDto.java b/backend/src/main/java/ch/puzzle/pcts/dto/memberoverview/certificate/MemberOverviewCertificateTypeDto.java new file mode 100644 index 000000000..b7993d570 --- /dev/null +++ b/backend/src/main/java/ch/puzzle/pcts/dto/memberoverview/certificate/MemberOverviewCertificateTypeDto.java @@ -0,0 +1,8 @@ +package ch.puzzle.pcts.dto.memberoverview.certificate; + +import io.swagger.v3.oas.annotations.media.Schema; + +public record MemberOverviewCertificateTypeDto( + @Schema(description = "The name of the certificate-type.", example = "Certified GitOps Associate", requiredMode = Schema.RequiredMode.REQUIRED, nullable = false, minLength = 1) String name, + @Schema(description = "An optional comment for the certificate-type.", example = "This is an awesome certificate-type.", requiredMode = Schema.RequiredMode.NOT_REQUIRED, nullable = true) String comment) { +} diff --git a/backend/src/main/java/ch/puzzle/pcts/dto/memberoverview/degree/MemberOverviewDegreeDto.java b/backend/src/main/java/ch/puzzle/pcts/dto/memberoverview/degree/MemberOverviewDegreeDto.java new file mode 100644 index 000000000..df51a9466 --- /dev/null +++ b/backend/src/main/java/ch/puzzle/pcts/dto/memberoverview/degree/MemberOverviewDegreeDto.java @@ -0,0 +1,18 @@ +package ch.puzzle.pcts.dto.memberoverview.degree; + +import io.swagger.v3.oas.annotations.media.Schema; +import java.time.LocalDate; + +public record MemberOverviewDegreeDto( + @Schema(description = "The unique identifier of the degree.", example = "1", accessMode = Schema.AccessMode.READ_ONLY) Long id, + + @Schema(description = "The name of the degree.", example = "Master of Computer Science", requiredMode = Schema.RequiredMode.REQUIRED, minLength = 1) String name, + + @Schema(description = "The type of the degree", exampleClasses = MemberOverviewDegreeTypeDto.class, requiredMode = Schema.RequiredMode.REQUIRED) MemberOverviewDegreeTypeDto type, + + @Schema(description = "The start date of the degree program.", example = "2018-09-01", requiredMode = Schema.RequiredMode.REQUIRED) LocalDate startDate, + + @Schema(description = "The end date of the degree program.", example = "2022-06-30") LocalDate endDate, + + @Schema(description = "Additional comments or notes about the degree.", example = "Graduated summa cum laude.") String comment) { +} diff --git a/backend/src/main/java/ch/puzzle/pcts/dto/memberoverview/degree/MemberOverviewDegreeTypeDto.java b/backend/src/main/java/ch/puzzle/pcts/dto/memberoverview/degree/MemberOverviewDegreeTypeDto.java new file mode 100644 index 000000000..5540f28b9 --- /dev/null +++ b/backend/src/main/java/ch/puzzle/pcts/dto/memberoverview/degree/MemberOverviewDegreeTypeDto.java @@ -0,0 +1,7 @@ +package ch.puzzle.pcts.dto.memberoverview.degree; + +import io.swagger.v3.oas.annotations.media.Schema; + +public record MemberOverviewDegreeTypeDto( + @Schema(description = "The name of the degree-type.", example = "Bachelor of Science", requiredMode = Schema.RequiredMode.REQUIRED, nullable = false, minLength = 1) String name) { +} diff --git a/backend/src/main/java/ch/puzzle/pcts/dto/memberoverview/experience/MemberOverviewExperienceDto.java b/backend/src/main/java/ch/puzzle/pcts/dto/memberoverview/experience/MemberOverviewExperienceDto.java new file mode 100644 index 000000000..b84317517 --- /dev/null +++ b/backend/src/main/java/ch/puzzle/pcts/dto/memberoverview/experience/MemberOverviewExperienceDto.java @@ -0,0 +1,14 @@ +package ch.puzzle.pcts.dto.memberoverview.experience; + +import io.swagger.v3.oas.annotations.media.Schema; +import java.time.LocalDate; + +public record MemberOverviewExperienceDto( + @Schema(description = "The unique identifier of the experience.", example = "1", requiredMode = Schema.RequiredMode.REQUIRED, nullable = false, accessMode = Schema.AccessMode.READ_ONLY) Long id, + @Schema(description = "The name or title of the experience", example = "Software Engineer Intern", requiredMode = Schema.RequiredMode.REQUIRED, nullable = false, minLength = 1) String name, + @Schema(description = "The employer or organization where the experience took place.", example = "TechCorp Inc.", requiredMode = Schema.RequiredMode.NOT_REQUIRED, nullable = true) String employer, + @Schema(description = "The type or category of the experience.", exampleClasses = MemberOverviewExperienceTypeDto.class, requiredMode = Schema.RequiredMode.REQUIRED, nullable = false) MemberOverviewExperienceTypeDto type, + @Schema(description = "Additional comments about the experience.", example = "Worked on backend API development using Spring Boot.", requiredMode = Schema.RequiredMode.NOT_REQUIRED, nullable = true) String comment, + @Schema(description = "The start date of the experience.", example = "2021-06-01", requiredMode = Schema.RequiredMode.REQUIRED, nullable = false) LocalDate startDate, + @Schema(description = "The end date of the experience.", example = "2021-12-31", requiredMode = Schema.RequiredMode.NOT_REQUIRED, nullable = true) LocalDate endDate) { +} diff --git a/backend/src/main/java/ch/puzzle/pcts/dto/memberoverview/experience/MemberOverviewExperienceTypeDto.java b/backend/src/main/java/ch/puzzle/pcts/dto/memberoverview/experience/MemberOverviewExperienceTypeDto.java new file mode 100644 index 000000000..a565a6eda --- /dev/null +++ b/backend/src/main/java/ch/puzzle/pcts/dto/memberoverview/experience/MemberOverviewExperienceTypeDto.java @@ -0,0 +1,7 @@ +package ch.puzzle.pcts.dto.memberoverview.experience; + +import io.swagger.v3.oas.annotations.media.Schema; + +public record MemberOverviewExperienceTypeDto( + @Schema(description = "The name of the experience-type.", example = "Management", requiredMode = Schema.RequiredMode.REQUIRED, nullable = false, minLength = 1) String name) { +} diff --git a/backend/src/main/java/ch/puzzle/pcts/dto/memberoverview/leadershipexperience/MemberOverviewLeadershipExperienceDto.java b/backend/src/main/java/ch/puzzle/pcts/dto/memberoverview/leadershipexperience/MemberOverviewLeadershipExperienceDto.java new file mode 100644 index 000000000..58985a179 --- /dev/null +++ b/backend/src/main/java/ch/puzzle/pcts/dto/memberoverview/leadershipexperience/MemberOverviewLeadershipExperienceDto.java @@ -0,0 +1,11 @@ +package ch.puzzle.pcts.dto.memberoverview.leadershipexperience; + +import io.swagger.v3.oas.annotations.media.Schema; + +public record MemberOverviewLeadershipExperienceDto( + @Schema(description = "The unique identifier of the leadership experience.", example = "1", requiredMode = Schema.RequiredMode.REQUIRED, accessMode = Schema.AccessMode.READ_ONLY, nullable = false) Long id, + + @Schema(description = "The type of leadership experience awarded to the member.", exampleClasses = MemberOverviewLeadershipExperienceTypeDto.class, requiredMode = Schema.RequiredMode.REQUIRED, nullable = false) MemberOverviewLeadershipExperienceTypeDto experience, + + @Schema(description = "An optional comment for the leadership experience.", example = "Completed via fast-track program", requiredMode = Schema.RequiredMode.NOT_REQUIRED, nullable = true) String comment) { +} diff --git a/backend/src/main/java/ch/puzzle/pcts/dto/memberoverview/leadershipexperience/MemberOverviewLeadershipExperienceTypeDto.java b/backend/src/main/java/ch/puzzle/pcts/dto/memberoverview/leadershipexperience/MemberOverviewLeadershipExperienceTypeDto.java new file mode 100644 index 000000000..d79108ee2 --- /dev/null +++ b/backend/src/main/java/ch/puzzle/pcts/dto/memberoverview/leadershipexperience/MemberOverviewLeadershipExperienceTypeDto.java @@ -0,0 +1,10 @@ +package ch.puzzle.pcts.dto.memberoverview.leadershipexperience; + +import ch.puzzle.pcts.model.certificatetype.CertificateKind; +import io.swagger.v3.oas.annotations.media.Schema; + +public record MemberOverviewLeadershipExperienceTypeDto( + @Schema(description = "The name of the leadership-experience-type.", example = "", requiredMode = Schema.RequiredMode.REQUIRED, minLength = 1) String name, + @Schema(description = "A optional comment for the leadership-experience-type.", example = "This is an awesome leadership-experience-type!", requiredMode = Schema.RequiredMode.NOT_REQUIRED, nullable = true) String comment, + @Schema(description = "The kind of leadership-experience-type which is either MILITARY_FUNCTION, YOUTH_AND_SPORT or LEADERSHIP_TRAINING", example = "LEADERSHIP_TRAINING", requiredMode = Schema.RequiredMode.REQUIRED) CertificateKind experienceKind) { +} diff --git a/backend/src/main/java/ch/puzzle/pcts/model/memberoverview/MemberOverview.java b/backend/src/main/java/ch/puzzle/pcts/model/memberoverview/MemberOverview.java index 11744633e..96f17d3a4 100644 --- a/backend/src/main/java/ch/puzzle/pcts/model/memberoverview/MemberOverview.java +++ b/backend/src/main/java/ch/puzzle/pcts/model/memberoverview/MemberOverview.java @@ -9,7 +9,7 @@ import java.time.LocalDate; @Entity -@Table(name = "member_overview_view") +@Table(name = "member_overview") public class MemberOverview implements Model { @Id @@ -33,12 +33,6 @@ public class MemberOverview implements Model { private String certificateTypeName; private String certificateTypeComment; - - private Long leadershipId; - private String leadershipComment; - - private String leadershipTypeName; - private String leadershipTypeComment; @Enumerated(EnumType.STRING) private CertificateKind leadershipTypeKind; @@ -46,6 +40,7 @@ public class MemberOverview implements Model { private String degreeName; private LocalDate degreeStartDate; private LocalDate degreeEndDate; + private String degreeComment; private String degreeTypeName; @@ -74,15 +69,12 @@ private MemberOverview(Builder builder) { certificateComment = trim(builder.certificateComment); certificateTypeName = trim(builder.certificateTypeName); certificateTypeComment = trim(builder.certificateTypeComment); - leadershipId = builder.leadershipId; - leadershipComment = trim(builder.leadershipComment); degreeId = builder.degreeId; degreeName = trim(builder.degreeName); degreeStartDate = builder.degreeStartDate; degreeEndDate = builder.degreeEndDate; - leadershipTypeName = trim(builder.leadershipTypeName); + degreeComment = builder.degreeComment; leadershipTypeKind = builder.leadershipTypeKind; - leadershipTypeComment = trim(builder.leadershipTypeComment); degreeTypeName = trim(builder.degreeTypeName); experienceId = builder.experienceId; experienceName = trim(builder.experienceName); @@ -219,22 +211,6 @@ public void setCertificateTypeComment(String certificateTypeComment) { this.certificateTypeComment = trim(certificateTypeComment); } - public Long getLeadershipId() { - return leadershipId; - } - - public void setLeadershipId(Long leadershipId) { - this.leadershipId = leadershipId; - } - - public String getLeadershipComment() { - return leadershipComment; - } - - public void setLeadershipComment(String leadershipComment) { - this.leadershipComment = trim(leadershipComment); - } - public Long getDegreeId() { return degreeId; } @@ -263,32 +239,24 @@ public LocalDate getDegreeEndDate() { return degreeEndDate; } - public void setDegreeEndDate(LocalDate degreeEndDate) { - this.degreeEndDate = degreeEndDate; + public String getDegreeComment() { + return degreeComment; } - public String getLeadershipTypeName() { - return leadershipTypeName; + public void setDegreeEndDate(LocalDate degreeEndDate) { + this.degreeEndDate = degreeEndDate; } - public void setLeadershipTypeName(String leadershipTypeName) { - this.leadershipTypeName = trim(leadershipTypeName); + public void setDegreeComment(String degreeComment) { + this.degreeComment = degreeComment; } - public CertificateKind getleadershipTypeKind() { + public CertificateKind getLeadershipTypeKind() { return leadershipTypeKind; } - public void setleadershipTypeKind(CertificateKind leadershipKind) { - this.leadershipTypeKind = leadershipKind; - } - - public String getLeadershipTypeComment() { - return leadershipTypeComment; - } - - public void setLeadershipTypeComment(String leadershipTypeComment) { - this.leadershipTypeComment = trim(leadershipTypeComment); + public void setLeadershipTypeKind(CertificateKind leadershipTypeKind) { + this.leadershipTypeKind = leadershipTypeKind; } public String getDegreeTypeName() { @@ -390,17 +358,13 @@ public static final class Builder { private String certificateTypeName; private String certificateTypeComment; - private Long leadershipId; - private String leadershipComment; - private Long degreeId; private String degreeName; private LocalDate degreeStartDate; private LocalDate degreeEndDate; + private String degreeComment; - private String leadershipTypeName; private CertificateKind leadershipTypeKind; - private String leadershipTypeComment; private String degreeTypeName; @@ -495,31 +459,11 @@ public Builder withCertificateTypeComment(String certificateTypeComment) { return this; } - public Builder withLeadershipId(Long leadershipId) { - this.leadershipId = leadershipId; - return this; - } - - public Builder withLeadershipComment(String leadershipComment) { - this.leadershipComment = trim(leadershipComment); - return this; - } - - public Builder withLeadershipTypeName(String leadershipTypeName) { - this.leadershipTypeName = trim(leadershipTypeName); - return this; - } - public Builder withleadershipTypeKind(CertificateKind leadershipTypeKind) { this.leadershipTypeKind = leadershipTypeKind; return this; } - public Builder withLeadershipTypeComment(String leadershipTypeComment) { - this.leadershipTypeComment = trim(leadershipTypeComment); - return this; - } - public Builder withDegreeId(Long degreeId) { this.degreeId = degreeId; return this; @@ -540,6 +484,11 @@ public Builder withDegreeEndDate(LocalDate degreeEndDate) { return this; } + public Builder withDegreeComment(String degreeComment) { + this.degreeComment = trim(degreeComment); + return this; + } + public Builder withDegreeTypeName(String degreeTypeName) { this.degreeTypeName = trim(degreeTypeName); return this; diff --git a/backend/src/main/resources/db/migration/V0_0_14__add_member_view.sql b/backend/src/main/resources/db/migration/V0_0_14__add_member_view.sql index c37f91407..cf9345da5 100644 --- a/backend/src/main/resources/db/migration/V0_0_14__add_member_view.sql +++ b/backend/src/main/resources/db/migration/V0_0_14__add_member_view.sql @@ -1,54 +1,56 @@ DROP VIEW IF EXISTS member_overview; CREATE VIEW member_overview AS -SELECT - m.id AS member_id, - m.first_name AS member_first_name, - m.last_name AS member_last_name, - m.abbreviation AS member_abbreviation, - m.employment_state AS member_employment_state, - m.date_of_hire AS member_date_of_hire, - m.birth_date AS member_birth_date, - ou.name AS organisation_unit_name, - - c.id AS certificate_id, - c.completed_at AS certificate_completed_at, - c.valid_until AS certificate_valid_until, - c.comment AS certificate_comment, - - ct.name AS certificate_type_name, - ct.comment AS certificate_type_comment, - - l.id AS leadership_id, - l.comment AS leadership_comment, - - lt.name AS leadership_type_name, - lt.comment AS leadership_type_comment, - lt.certificate_kind AS leadership_type_kind, - - d.id AS degree_id, - d.name AS degree_name, - d.start_date AS degree_start_date, - d.end_date AS degree_end_date, - - dt.name AS degree_type_name, - - e.id AS experience_id, - e.name AS experience_name, - e.employer AS experience_employer, - e.start_date AS experience_start_date, - e.end_date AS experience_end_date, - e.comment AS experience_comment, - - et.name AS experience_type_name +SELECT m.id AS member_id, + m.first_name, + m.last_name, + m.abbreviation, + m.employment_state, + m.date_of_hire, + m.birth_date, + + ou.name AS organisation_unit_name, + + c.id AS certificate_id, + c.completed_at, + c.valid_until, + c.comment AS certificate_comment, + + ct.name AS certificate_type_name, + ct.comment AS certificate_type_comment, + ct.certificate_kind AS certificate_type_kind, + + d.id AS degree_id, + d.name AS degree_name, + d.start_date AS degree_start_date, + d.end_date AS degree_end_date, + d.comment AS degree_comment, + dt.name AS degree_type_name, + + e.id AS experience_id, + e.name AS experience_name, + e.employer, + e.start_date AS experience_start_date, + e.end_date AS experience_end_date, + e.comment AS experience_comment, + et.name AS experience_type_name FROM member m - LEFT JOIN organisation_unit ou ON ou.id = m.organisation_unit - LEFT JOIN certificate c ON c.member_id = m.id - LEFT JOIN certificate_type ct ON ct.id = c.certificate_type_id - LEFT JOIN certificate l ON l.member_id = m.id - LEFT JOIN certificate_type lt ON lt.id = l.certificate_type_id - LEFT JOIN degree d ON d.member_id = m.id - LEFT JOIN degree_type dt ON dt.id = d.degree_type_id - LEFT JOIN experience e ON e.member_id = m.id - LEFT JOIN experience_type et ON et.id = e.experience_type_id + + LEFT JOIN organisation_unit ou + ON ou.id = m.organisation_unit + + LEFT JOIN certificate c + ON c.member_id = m.id + LEFT JOIN certificate_type ct + ON ct.id = c.certificate_type_id + + LEFT JOIN degree d + ON d.member_id = m.id + LEFT JOIN degree_type dt + ON dt.id = d.degree_type_id + + LEFT JOIN experience e + ON e.member_id = m.id + LEFT JOIN experience_type et + ON et.id = e.experience_type_id; From 5e66b7754dad57ce2310ebb281d1f86c376a18cc Mon Sep 17 00:00:00 2001 From: Manuel Date: Fri, 19 Dec 2025 16:42:08 +0100 Subject: [PATCH 10/30] refactor(backend): Fix mapper and model to actually work for now #50 --- .../pcts/mapper/MemberOverviewMapper.java | 174 +++++++----------- .../model/memberoverview/MemberOverview.java | 1 + .../db/migration/V0_0_14__add_member_view.sql | 76 ++++---- 3 files changed, 108 insertions(+), 143 deletions(-) diff --git a/backend/src/main/java/ch/puzzle/pcts/mapper/MemberOverviewMapper.java b/backend/src/main/java/ch/puzzle/pcts/mapper/MemberOverviewMapper.java index 8c97e1f67..e7dfd403f 100644 --- a/backend/src/main/java/ch/puzzle/pcts/mapper/MemberOverviewMapper.java +++ b/backend/src/main/java/ch/puzzle/pcts/mapper/MemberOverviewMapper.java @@ -1,45 +1,41 @@ package ch.puzzle.pcts.mapper; -import ch.puzzle.pcts.dto.calculation.CalculationDto; -import ch.puzzle.pcts.dto.certificate.CertificateDto; -import ch.puzzle.pcts.dto.certificatetype.CertificateTypeDto; -import ch.puzzle.pcts.dto.degree.DegreeDto; -import ch.puzzle.pcts.dto.degreetype.DegreeTypeDto; -import ch.puzzle.pcts.dto.experience.ExperienceDto; -import ch.puzzle.pcts.dto.experiencetype.ExperienceTypeDto; -import ch.puzzle.pcts.dto.leadershipexperience.LeadershipExperienceDto; -import ch.puzzle.pcts.dto.leadershipexperiencetype.LeadershipExperienceTypeDto; -import ch.puzzle.pcts.dto.member.MemberDto; -import ch.puzzle.pcts.dto.memberoverview.MemberCvDto; -import ch.puzzle.pcts.dto.memberoverview.MemberOverviewDto; -import ch.puzzle.pcts.dto.organisationunit.OrganisationUnitDto; -import ch.puzzle.pcts.dto.role.RoleDto; +import ch.puzzle.pcts.dto.memberoverview.*; +import ch.puzzle.pcts.dto.memberoverview.certificate.MemberOverviewCertificateDto; +import ch.puzzle.pcts.dto.memberoverview.certificate.MemberOverviewCertificateTypeDto; +import ch.puzzle.pcts.dto.memberoverview.degree.MemberOverviewDegreeDto; +import ch.puzzle.pcts.dto.memberoverview.degree.MemberOverviewDegreeTypeDto; +import ch.puzzle.pcts.dto.memberoverview.experience.MemberOverviewExperienceDto; +import ch.puzzle.pcts.dto.memberoverview.experience.MemberOverviewExperienceTypeDto; +import ch.puzzle.pcts.dto.memberoverview.leadershipexperience.MemberOverviewLeadershipExperienceDto; +import ch.puzzle.pcts.dto.memberoverview.leadershipexperience.MemberOverviewLeadershipExperienceTypeDto; +import ch.puzzle.pcts.model.certificatetype.CertificateKind; import ch.puzzle.pcts.model.memberoverview.MemberOverview; -import java.util.*; +import java.util.List; import java.util.stream.Collectors; import org.springframework.stereotype.Component; @Component public class MemberOverviewMapper { - public MemberOverviewMapper() { - } - public MemberOverviewDto toDto(List entities) { + if (entities == null || entities.isEmpty()) { + return null; + } + MemberOverview first = entities.getFirst(); - MemberDto memberDto = new MemberDto(first.getId(), - first.getFirstName(), - first.getLastName(), - first.getEmploymentState(), - first.getAbbreviation(), - first.getDateOfHire(), - first.getBirthDate(), - new OrganisationUnitDto(first.getOrganisationUnitId(), - first.getOrganisationUnitName())); + MemberOverviewMemberDto memberDto = new MemberOverviewMemberDto(first.getMemberId(), + first.getFirstName(), + first.getLastName(), + first.getEmploymentState(), + first.getAbbreviation(), + first.getDateOfHire(), + first.getBirthDate(), + first.getOrganisationUnitName()); - List degrees = entities + List degrees = entities .stream() .filter(e -> e.getDegreeId() != null) .collect(Collectors.groupingBy(MemberOverview::getDegreeId)) @@ -47,24 +43,16 @@ public MemberOverviewDto toDto(List entities) { .stream() .map(group -> { MemberOverview e = group.getFirst(); - - return new DegreeDto(e.getDegreeId(), - memberDto, - e.getDegreeName(), - e.getDegreeInstitution(), - e.getDegreeCompleted(), - new DegreeTypeDto(e.getDegreeTypeId(), - e.getDegreeTypeName(), - e.getDegreeHighlyRelevantPoints(), - e.getDegreeLimitedRelevantPoints(), - e.getDegreeLittleRelevantPoints()), - e.getDegreeStartDate(), - e.getDegreeEndDate(), - e.getDegreeComment()); + return new MemberOverviewDegreeDto(e.getDegreeId(), + e.getDegreeName(), + new MemberOverviewDegreeTypeDto(e.getDegreeTypeName()), + e.getDegreeStartDate(), + e.getDegreeEndDate(), + e.getDegreeComment()); }) .toList(); - List experiences = entities + List experiences = entities .stream() .filter(e -> e.getExperienceId() != null) .collect(Collectors.groupingBy(MemberOverview::getExperienceId)) @@ -72,91 +60,59 @@ public MemberOverviewDto toDto(List entities) { .stream() .map(group -> { MemberOverview e = group.getFirst(); - - return new ExperienceDto(e.getExperienceId(), - memberDto, - e.getExperienceName(), - e.getExperienceEmployer(), - e.getExperiencePercent(), - new ExperienceTypeDto(e.getExperienceTypeId(), - e.getExperienceTypeName(), - e.getExperienceHighlyRelevantPoints(), - e.getExperienceLimitedRelevantPoints(), - e.getExperienceLittleRelevantPoints()), - e.getExperienceComment(), - e.getExperienceStartDate(), - e.getExperienceEndDate()); + return new MemberOverviewExperienceDto(e.getExperienceId(), + e.getExperienceName(), + e.getExperienceEmployer(), + new MemberOverviewExperienceTypeDto(e + .getExperienceTypeName()), + e.getExperienceComment(), + e.getExperienceStartDate(), + e.getExperienceEndDate()); }) .toList(); - Map> certificateGroups = entities + List certificates = entities .stream() - .filter(e -> e.getCertificateId() != null) - .collect(Collectors.groupingBy(MemberOverview::getCertificateId)); - - List certificates = certificateGroups.values().stream().map(certRows -> { - - MemberOverview certData = certRows.get(0); - - List tags = certRows - .stream() - .map(MemberOverview::getTagName) - .filter(Objects::nonNull) - .distinct() - .toList(); - - return new CertificateDto(certData.getCertificateId(), - memberDto, - new CertificateTypeDto(certData.getCertificateTypeId(), - certData.getCertificateTypeName(), - certData.getCertificateTypePoints(), - certData.getCertificateTypeComment(), - tags), - certData.getCertificateCompletedAt(), - certData.getCertificateValidUntil(), - certData.getCertificateComment()); - }).toList(); - - List leadershipExperiences = entities - .stream() - .filter(e -> e.getLeadershipId() != null) - .collect(Collectors.groupingBy(MemberOverview::getLeadershipId)) + .filter(e -> e.getCertificateId() != null + && CertificateKind.CERTIFICATE.equals(e.getLeadershipTypeKind())) + .collect(Collectors.groupingBy(MemberOverview::getCertificateId)) .values() .stream() .map(group -> { MemberOverview e = group.getFirst(); - - return new LeadershipExperienceDto(e.getLeadershipId(), - memberDto, - new LeadershipExperienceTypeDto(e.getLeadershipTypeId(), - e.getLeadershipTypeName(), - e.getLeadershipTypePoints(), - e.getLeadershipTypeComment(), - e.getLeadershipKind()), - e.getLeadershipComment()); + return new MemberOverviewCertificateDto(e.getCertificateId(), + new MemberOverviewCertificateTypeDto(e + .getCertificateTypeName(), + e + .getCertificateTypeComment()), + e.getCertificateCompletedAt(), + e.getCertificateValidUntil(), + e.getCertificateComment()); }) .toList(); - List calculations = entities + List LeadershipExperiences = entities .stream() - .filter(e -> e.getCalculationId() != null) - .collect(Collectors.groupingBy(MemberOverview::getCalculationId)) + .filter(e -> e.getCertificateId() != null + && !CertificateKind.CERTIFICATE.equals(e.getLeadershipTypeKind())) + .collect(Collectors.groupingBy(MemberOverview::getCertificateId)) .values() .stream() .map(group -> { MemberOverview e = group.getFirst(); - - return new CalculationDto(e.getCalculationId(), - memberDto, - new RoleDto(e.getRoleId(), e.getRoleName(), e.getRoleIsManagement()), - e.getCalculationState(), - e.getCalculationPublicationDate(), - e.getCalculationPublicizedBy()); + return new MemberOverviewLeadershipExperienceDto(e.getCertificateId(), + new MemberOverviewLeadershipExperienceTypeDto(e + .getCertificateTypeName(), + e + .getCertificateTypeComment(), + e + .getLeadershipTypeKind()), + e.getCertificateComment()); }) .toList(); - MemberCvDto cvDto = new MemberCvDto(degrees, experiences, certificates, leadershipExperiences); + MemberCvDto cvDto = new MemberCvDto(degrees, experiences, certificates, LeadershipExperiences); - return new MemberOverviewDto(memberDto, cvDto, calculations); + return new MemberOverviewDto(memberDto, cvDto); } } diff --git a/backend/src/main/java/ch/puzzle/pcts/model/memberoverview/MemberOverview.java b/backend/src/main/java/ch/puzzle/pcts/model/memberoverview/MemberOverview.java index 96f17d3a4..d88a66d7d 100644 --- a/backend/src/main/java/ch/puzzle/pcts/model/memberoverview/MemberOverview.java +++ b/backend/src/main/java/ch/puzzle/pcts/model/memberoverview/MemberOverview.java @@ -13,6 +13,7 @@ public class MemberOverview implements Model { @Id + @Column(name = "unique_row_id") private Long uniqueRowId; private Long memberId; diff --git a/backend/src/main/resources/db/migration/V0_0_14__add_member_view.sql b/backend/src/main/resources/db/migration/V0_0_14__add_member_view.sql index cf9345da5..d66557cc0 100644 --- a/backend/src/main/resources/db/migration/V0_0_14__add_member_view.sql +++ b/backend/src/main/resources/db/migration/V0_0_14__add_member_view.sql @@ -1,42 +1,50 @@ DROP VIEW IF EXISTS member_overview; CREATE VIEW member_overview AS -SELECT m.id AS member_id, - m.first_name, - m.last_name, - m.abbreviation, - m.employment_state, - m.date_of_hire, - m.birth_date, - - ou.name AS organisation_unit_name, - - c.id AS certificate_id, - c.completed_at, - c.valid_until, - c.comment AS certificate_comment, - - ct.name AS certificate_type_name, - ct.comment AS certificate_type_comment, - ct.certificate_kind AS certificate_type_kind, - - d.id AS degree_id, - d.name AS degree_name, - d.start_date AS degree_start_date, - d.end_date AS degree_end_date, - d.comment AS degree_comment, - dt.name AS degree_type_name, - - e.id AS experience_id, - e.name AS experience_name, - e.employer, - e.start_date AS experience_start_date, - e.end_date AS experience_end_date, - e.comment AS experience_comment, - et.name AS experience_type_name +SELECT + ROW_NUMBER() OVER ( + ORDER BY + m.id, + c.id, + d.id, + e.id + ) AS unique_row_id, -FROM member m + m.id AS member_id, + m.first_name, + m.last_name, + m.abbreviation, + m.employment_state, + m.date_of_hire, + m.birth_date, + + ou.name AS organisation_unit_name, + + c.id AS certificate_id, + c.completed_at AS certificate_completed_at, + c.valid_until AS certificate_valid_until, + c.comment AS certificate_comment, + + ct.name AS certificate_type_name, + ct.comment AS certificate_type_comment, + ct.certificate_kind AS leadership_type_kind, + d.id AS degree_id, + d.name AS degree_name, + d.start_date AS degree_start_date, + d.end_date AS degree_end_date, + d.comment AS degree_comment, + dt.name AS degree_type_name, + + e.id AS experience_id, + e.name AS experience_name, + e.employer AS experience_employer, + e.start_date AS experience_start_date, + e.end_date AS experience_end_date, + e.comment AS experience_comment, + et.name AS experience_type_name + +FROM member m LEFT JOIN organisation_unit ou ON ou.id = m.organisation_unit From 6f8904d553f64cbd6fab2516dba0a5275b2c6096 Mon Sep 17 00:00:00 2001 From: Nevio Di Gennaro Date: Mon, 22 Dec 2025 17:21:47 +0100 Subject: [PATCH 11/30] test: add a prototype of unified testData #50 --- .../model/memberoverview/MemberOverview.java | 2 +- .../CalculationPersistenceServiceIT.java | 21 +- .../CertificatePersistenceServiceIT.java | 171 +---------- .../CertificateTypePersistenceServiceIT.java | 45 +-- .../DegreePersistenceServiceIT.java | 102 +------ .../DegreeTypePersistenceServiceIT.java | 13 +- .../ExperiencePersistenceServiceIT.java | 45 +-- .../ExperienceTypePersistenceServiceIT.java | 13 +- .../LeadershipTypePersistenceServiceIT.java | 41 +-- .../MemberPersistenceServiceIT.java | 34 +-- .../OrganisationUnitPersistenceServiceIT.java | 3 +- .../persistence/RolePersistenceServiceIT.java | 3 +- .../persistence/TagPersistenceServiceIT.java | 3 +- .../java/ch/puzzle/pcts/util/TestData.java | 278 ++++++++++++++++++ 14 files changed, 322 insertions(+), 452 deletions(-) create mode 100644 backend/src/test/java/ch/puzzle/pcts/util/TestData.java diff --git a/backend/src/main/java/ch/puzzle/pcts/model/memberoverview/MemberOverview.java b/backend/src/main/java/ch/puzzle/pcts/model/memberoverview/MemberOverview.java index d88a66d7d..8df079259 100644 --- a/backend/src/main/java/ch/puzzle/pcts/model/memberoverview/MemberOverview.java +++ b/backend/src/main/java/ch/puzzle/pcts/model/memberoverview/MemberOverview.java @@ -249,7 +249,7 @@ public void setDegreeEndDate(LocalDate degreeEndDate) { } public void setDegreeComment(String degreeComment) { - this.degreeComment = degreeComment; + this.degreeComment = trim(degreeComment); } public CertificateKind getLeadershipTypeKind() { diff --git a/backend/src/test/java/ch/puzzle/pcts/service/persistence/CalculationPersistenceServiceIT.java b/backend/src/test/java/ch/puzzle/pcts/service/persistence/CalculationPersistenceServiceIT.java index 4d8b72947..52b27abfc 100644 --- a/backend/src/test/java/ch/puzzle/pcts/service/persistence/CalculationPersistenceServiceIT.java +++ b/backend/src/test/java/ch/puzzle/pcts/service/persistence/CalculationPersistenceServiceIT.java @@ -8,6 +8,7 @@ import ch.puzzle.pcts.model.member.Member; import ch.puzzle.pcts.model.role.Role; import ch.puzzle.pcts.repository.CalculationRepository; +import ch.puzzle.pcts.util.TestData; import jakarta.transaction.Transactional; import java.time.LocalDate; import java.util.List; @@ -43,25 +44,7 @@ Calculation getModel() { @Override List getAll() { - return List - .of(new Calculation(1L, - memberPersistenceServiceIT.getAll().getFirst(), - rolePersistenceServiceIT.getAll().getLast(), - CalculationState.DRAFT, - LocalDate.of(2025, 1, 14), - "Ldap User"), - new Calculation(2L, - memberPersistenceServiceIT.getAll().getLast(), - rolePersistenceServiceIT.getAll().getLast(), - CalculationState.ARCHIVED, - LocalDate.of(2025, 1, 14), - "Ldap User 2"), - new Calculation(3L, - memberPersistenceServiceIT.getAll().getLast(), - rolePersistenceServiceIT.getAll().getLast(), - CalculationState.ACTIVE, - null, - null)); + return TestData.CALCULATIONS; } @DisplayName("Should only have one active Calculation after save when member already has active Calculation for the same role.") diff --git a/backend/src/test/java/ch/puzzle/pcts/service/persistence/CertificatePersistenceServiceIT.java b/backend/src/test/java/ch/puzzle/pcts/service/persistence/CertificatePersistenceServiceIT.java index 9b76e0391..c7e4da0a2 100644 --- a/backend/src/test/java/ch/puzzle/pcts/service/persistence/CertificatePersistenceServiceIT.java +++ b/backend/src/test/java/ch/puzzle/pcts/service/persistence/CertificatePersistenceServiceIT.java @@ -5,7 +5,6 @@ import ch.puzzle.pcts.model.certificate.Certificate; import ch.puzzle.pcts.model.certificatetype.CertificateKind; import ch.puzzle.pcts.model.certificatetype.CertificateType; -import ch.puzzle.pcts.model.certificatetype.Tag; import ch.puzzle.pcts.model.member.EmploymentState; import ch.puzzle.pcts.model.member.Member; import ch.puzzle.pcts.model.organisationunit.OrganisationUnit; @@ -13,13 +12,12 @@ import ch.puzzle.pcts.repository.CertificateTypeRepository; import ch.puzzle.pcts.repository.MemberRepository; import ch.puzzle.pcts.repository.OrganisationUnitRepository; +import ch.puzzle.pcts.util.TestData; import jakarta.transaction.Transactional; import java.math.BigDecimal; -import java.time.Instant; import java.time.LocalDate; -import java.time.LocalDateTime; -import java.time.ZoneOffset; import java.util.List; +import java.util.Optional; import java.util.Set; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; @@ -29,172 +27,33 @@ class CertificatePersistenceServiceIT extends PersistenceBaseIT { - @Autowired - CertificatePersistenceServiceIT(CertificatePersistenceService service) { - super(service); - } - @Autowired private MemberRepository memberRepository; - @Autowired private CertificateTypeRepository certificateTypeRepository; - @Autowired private OrganisationUnitRepository organisationUnitRepository; - @Override - Certificate getModel() { - return createCertificate(null, - createMember(1L, - "Member 1", - "M1", - LocalDate.of(2021, 7, 15), - LocalDate.of(1999, 8, 10), - createOrganisationUnit(1L, - "OrganisationUnit 1", - LocalDateTime - .ofInstant(Instant.EPOCH, - ZoneOffset.UTC))), - createCertificateType(1L, - "Certificate Type 1", - new BigDecimal("5.5"), - "This is Certificate 1", - Set.of(new Tag(1L, "Tag 1"))), - LocalDate.of(2021, 7, 15), - LocalDate.of(2021, 7, 15), - "Comment"); + @Autowired + CertificatePersistenceServiceIT(CertificatePersistenceService service) { + super(service); } @Override - List getAll() { - Certificate certificate1 = createCertificate(1L, - createMember(1L, - "Member 1", - "M1", - LocalDate.of(2021, 7, 15), - LocalDate.of(1999, 8, 10), - createOrganisationUnit(1L, - "OrganisationUnit 1", - LocalDateTime - .of(1970, - 1, - 1, - 0, - 0))), - createCertificateType(1L, - "Certificate Type 1", - new BigDecimal("5.5"), - "This is Certificate 1", - Set.of(new Tag(1L, "Tag 1"))), - LocalDate.of(2023, 1, 15), - LocalDate.of(2025, 1, 14), - "Completed first aid training."); - Certificate certificate2 = createCertificate(2L, - createMember(2L, - "Member 2", - "M2", - LocalDate.of(2020, 6, 1), - LocalDate.of(1998, 3, 3), - createOrganisationUnit(2L, - "OrganisationUnit 2", - null)), - createCertificateType(2L, - "Certificate Type 2", - new BigDecimal("1"), - "This is Certificate 2", - Set.of(new Tag(2L, "Longer tag name"))), - LocalDate.of(2022, 11, 1), - null, - "Completed first aid training."); - Certificate certificate3 = createCertificate(3L, - createMember(2L, - "Member 2", - "M2", - LocalDate.of(2020, 6, 1), - LocalDate.of(1998, 3, 3), - createOrganisationUnit(2L, - "OrganisationUnit 2", - null)), - createCertificateType(1L, - "Certificate Type 1", - new BigDecimal("5.5"), - "This is Certificate 1", - Set.of(new Tag(1L, "Tag 1"))), - LocalDate.of(2023, 1, 15), - LocalDate.of(2025, 1, 14), - null); - Certificate certificate4 = createCertificate(4L, - createMember(1L, - "Member 1", - "M1", - LocalDate.of(2021, 7, 15), - LocalDate.of(1999, 8, 10), - createOrganisationUnit(1L, - "OrganisationUnit 1", - LocalDateTime - .of(1970, - 1, - 1, - 0, - 0))), - createCertificateType(2L, - "Certificate Type 2", - new BigDecimal("1"), - "This is Certificate 2", - Set.of(new Tag(2L, "Longer tag name"))), - LocalDate.of(2010, 8, 12), - LocalDate.of(2023, 3, 25), - "Left organization."); - - return List.of(certificate1, certificate2, certificate3, certificate4); - } - - private Member createMember(Long id, String firstName, String abbreviation, LocalDate dateOfHire, - LocalDate birthDate, OrganisationUnit organisationUnit) { - return Member.Builder + Certificate getModel() { + return Certificate.Builder .builder() - .withId(id) - .withFirstName(firstName) - .withLastName("Test") - .withEmploymentState(EmploymentState.MEMBER) - .withAbbreviation(abbreviation) - .withDateOfHire(dateOfHire) - .withBirthDate(birthDate) - .withOrganisationUnit(organisationUnit) + .withId(null) + .withMember(TestData.MEMBER_1) + .withCompletedAt(LocalDate.of(2021, 7, 15)) + .withValidUntil(LocalDate.of(2021, 7, 15)) + .withCertificateType(TestData.CERT_TYPE_1) .build(); } - private OrganisationUnit createOrganisationUnit(Long id, String name, LocalDateTime deletedAt) { - OrganisationUnit organisationUnit = new OrganisationUnit(id, name); - organisationUnit.setDeletedAt(deletedAt); - return organisationUnit; - } - - private CertificateType createCertificateType(Long id, String name, BigDecimal points, String comment, - Set tags) { - CertificateType certificateType = new CertificateType(); - certificateType.setId(id); - certificateType.setName(name); - certificateType.setPoints(points); - certificateType.setComment(comment); - certificateType.setTags(tags); - certificateType.setCertificateKind(CertificateKind.CERTIFICATE); - certificateType.setDeletedAt(null); - return certificateType; - } - - private Certificate createCertificate(Long id, Member member, CertificateType certificateType, - LocalDate completedAt, LocalDate validUntil, String comment) { - return Certificate.Builder - .builder() - .withId(id) - .withMember(member) - .withCertificateType(certificateType) - .withCompletedAt(completedAt) - .withValidUntil(validUntil) - .withComment(comment) - .build(); + @Override + List getAll() { + return TestData.CERTIFICATES; } @Test diff --git a/backend/src/test/java/ch/puzzle/pcts/service/persistence/CertificateTypePersistenceServiceIT.java b/backend/src/test/java/ch/puzzle/pcts/service/persistence/CertificateTypePersistenceServiceIT.java index bd946240d..3d1f55880 100644 --- a/backend/src/test/java/ch/puzzle/pcts/service/persistence/CertificateTypePersistenceServiceIT.java +++ b/backend/src/test/java/ch/puzzle/pcts/service/persistence/CertificateTypePersistenceServiceIT.java @@ -11,6 +11,7 @@ import ch.puzzle.pcts.model.certificatetype.CertificateType; import ch.puzzle.pcts.model.certificatetype.Tag; import ch.puzzle.pcts.repository.CertificateTypeRepository; +import ch.puzzle.pcts.util.TestData; import java.math.BigDecimal; import java.util.List; import java.util.Map; @@ -41,49 +42,7 @@ CertificateType getModel() { @Override List getAll() { - return List - .of(new CertificateType(1L, - "Certificate Type 1", - BigDecimal.valueOf(5.5), - "This is Certificate 1", - Set.of(new Tag(1L, "Tag 1")), - CertificateKind.CERTIFICATE), - new CertificateType(2L, - "Certificate Type 2", - BigDecimal.valueOf(1), - "This is Certificate 2", - Set.of(new Tag(2L, "Longer tag name")), - CertificateKind.CERTIFICATE), - new CertificateType(3L, - "Certificate Type 3", - BigDecimal.valueOf(3), - "This is Certificate 3", - Set.of(), - CertificateKind.CERTIFICATE), - new CertificateType(4L, - "Certificate Type 4", - BigDecimal.valueOf(0.5), - "This is Certificate 4", - Set.of(), - CertificateKind.CERTIFICATE), - new CertificateType(5L, - "LeadershipExperience Type 1", - BigDecimal.valueOf(5.5), - "This is LeadershipExperience 1", - Set.of(), - CertificateKind.MILITARY_FUNCTION), - new CertificateType(6L, - "LeadershipExperience Type 2", - BigDecimal.valueOf(1), - "This is LeadershipExperience 2", - Set.of(), - CertificateKind.YOUTH_AND_SPORT), - new CertificateType(7L, - "LeadershipExperience Type 3", - BigDecimal.valueOf(3), - "This is LeadershipExperience 3", - Set.of(), - CertificateKind.LEADERSHIP_TRAINING)); + return TestData.CERTIFICATE_TYPES; } @Override diff --git a/backend/src/test/java/ch/puzzle/pcts/service/persistence/DegreePersistenceServiceIT.java b/backend/src/test/java/ch/puzzle/pcts/service/persistence/DegreePersistenceServiceIT.java index eab7ad6ea..f87c082cb 100644 --- a/backend/src/test/java/ch/puzzle/pcts/service/persistence/DegreePersistenceServiceIT.java +++ b/backend/src/test/java/ch/puzzle/pcts/service/persistence/DegreePersistenceServiceIT.java @@ -1,16 +1,9 @@ package ch.puzzle.pcts.service.persistence; import ch.puzzle.pcts.model.degree.Degree; -import ch.puzzle.pcts.model.degreetype.DegreeType; -import ch.puzzle.pcts.model.member.EmploymentState; -import ch.puzzle.pcts.model.member.Member; -import ch.puzzle.pcts.model.organisationunit.OrganisationUnit; import ch.puzzle.pcts.repository.DegreeRepository; -import java.math.BigDecimal; -import java.time.Instant; +import ch.puzzle.pcts.util.TestData; import java.time.LocalDate; -import java.time.LocalDateTime; -import java.time.ZoneOffset; import java.util.List; import org.springframework.beans.factory.annotation.Autowired; @@ -23,35 +16,14 @@ public class DegreePersistenceServiceIT extends PersistenceBaseIT getAll() { - OrganisationUnit deletedOrganisationUnit = new OrganisationUnit(1L, "OrganisationUnit 1"); - deletedOrganisationUnit.setDeletedAt(LocalDateTime.ofInstant(Instant.EPOCH, ZoneOffset.UTC)); - - OrganisationUnit organisationUnit2 = new OrganisationUnit(2L, "OrganisationUnit 2"); - - DegreeType degreeType1 = new DegreeType(1L, - "Degree type 1", - new BigDecimal("120.55"), - new BigDecimal("60"), - new BigDecimal("15.45")); - - DegreeType degreeType2 = new DegreeType(2L, - "Degree type 2", - new BigDecimal("12"), - new BigDecimal("3.961"), - new BigDecimal("3")); - - Member member1 = Member.Builder - .builder() - .withId(1L) - .withFirstName("Member 1") - .withLastName("Test") - .withEmploymentState(EmploymentState.MEMBER) - .withAbbreviation("M1") - .withDateOfHire(LocalDate.of(2021, 7, 15)) - .withBirthDate(LocalDate.of(1999, 8, 10)) - .withOrganisationUnit(deletedOrganisationUnit) - .build(); - - Member member2 = Member.Builder - .builder() - .withId(2L) - .withFirstName("Member 2") - .withLastName("Test") - .withEmploymentState(EmploymentState.MEMBER) - .withAbbreviation("M2") - .withDateOfHire(LocalDate.of(2020, 6, 1)) - .withBirthDate(LocalDate.of(1998, 3, 3)) - .withOrganisationUnit(organisationUnit2) - .build(); - - return List - .of(Degree.Builder - .builder() - .withId(1L) - .withMember(member1) - .withName("Degree 1") - .withInstitution("Institution") - .withCompleted(true) - .withDegreeType(degreeType1) - .withStartDate(LocalDate.of(2015, 9, 1)) - .withEndDate(LocalDate.of(2020, 6, 1)) - .withComment("Comment") - .build(), - - Degree.Builder - .builder() - .withId(2L) - .withMember(member2) - .withName("Degree 2") - .withInstitution("Institution") - .withCompleted(false) - .withDegreeType(degreeType2) - .withStartDate(LocalDate.of(2016, 9, 1)) - .withEndDate(LocalDate.of(2019, 6, 30)) - .withComment("Comment") - .build()); + return TestData.DEGREES; } } diff --git a/backend/src/test/java/ch/puzzle/pcts/service/persistence/DegreeTypePersistenceServiceIT.java b/backend/src/test/java/ch/puzzle/pcts/service/persistence/DegreeTypePersistenceServiceIT.java index ab526be8d..576854f85 100644 --- a/backend/src/test/java/ch/puzzle/pcts/service/persistence/DegreeTypePersistenceServiceIT.java +++ b/backend/src/test/java/ch/puzzle/pcts/service/persistence/DegreeTypePersistenceServiceIT.java @@ -2,6 +2,7 @@ import ch.puzzle.pcts.model.degreetype.DegreeType; import ch.puzzle.pcts.repository.DegreeTypeRepository; +import ch.puzzle.pcts.util.TestData; import java.math.BigDecimal; import java.util.List; import org.springframework.beans.factory.annotation.Autowired; @@ -26,16 +27,6 @@ DegreeType getModel() { @Override List getAll() { - return List - .of(new DegreeType(1L, - "Degree type 1", - BigDecimal.valueOf(120.55), - BigDecimal.valueOf(60), - BigDecimal.valueOf(15.45)), - new DegreeType(2L, - "Degree type 2", - BigDecimal.valueOf(12), - BigDecimal.valueOf(3.961), - BigDecimal.valueOf(3))); + return TestData.DEGREE_TYPES; } } \ No newline at end of file diff --git a/backend/src/test/java/ch/puzzle/pcts/service/persistence/ExperiencePersistenceServiceIT.java b/backend/src/test/java/ch/puzzle/pcts/service/persistence/ExperiencePersistenceServiceIT.java index 679c5183c..f3da1b286 100644 --- a/backend/src/test/java/ch/puzzle/pcts/service/persistence/ExperiencePersistenceServiceIT.java +++ b/backend/src/test/java/ch/puzzle/pcts/service/persistence/ExperiencePersistenceServiceIT.java @@ -1,37 +1,29 @@ package ch.puzzle.pcts.service.persistence; import ch.puzzle.pcts.model.experience.Experience; -import ch.puzzle.pcts.model.experiencetype.ExperienceType; -import ch.puzzle.pcts.model.member.Member; import ch.puzzle.pcts.repository.ExperienceRepository; +import ch.puzzle.pcts.util.TestData; import java.time.LocalDate; -import java.util.ArrayList; import java.util.List; import org.springframework.beans.factory.annotation.Autowired; public class ExperiencePersistenceServiceIT extends PersistenceBaseIT { - private final List members; - private final List experienceTypes; @Autowired - ExperiencePersistenceServiceIT(ExperiencePersistenceService service, - MemberPersistenceService memberPersistenceService, - ExperienceTypePersistenceService experienceTypePersistenceService) { + ExperiencePersistenceServiceIT(ExperiencePersistenceService service) { super(service); - this.members = new MemberPersistenceServiceIT(memberPersistenceService).getAll(); - this.experienceTypes = new ExperienceTypePersistenceServiceIT(experienceTypePersistenceService).getAll(); } @Override Experience getModel() { return new Experience.Builder() - .withMember(members.getFirst()) + .withMember(TestData.MEMBER_1) .withName("Experience 4") .withEmployer("Employer 4") .withPercent(100) - .withType(experienceTypes.getFirst()) + .withType(TestData.EXP_TYPE_1) .withComment("Comment test 4") .withStartDate(LocalDate.of(2021, 7, 15)) .withEndDate(LocalDate.of(2022, 7, 15)) @@ -39,33 +31,6 @@ Experience getModel() { } List getAll() { - List experiences = new ArrayList<>(); - experiences - .add(new Experience.Builder() - .withId(2L) - .withMember(members.getFirst()) - .withName("Experience 2") - .withEmployer("Employer 2") - .withPercent(80) - .withType(experienceTypes.get(1)) - .withComment("Comment test 2") - .withStartDate(LocalDate.of(2022, 7, 16)) - .withEndDate(LocalDate.of(2023, 7, 15)) - .build()); - - experiences - .add(new Experience.Builder() - .withId(3L) - .withMember(members.get(1)) - .withName("Experience 3") - .withEmployer("Employer 3") - .withPercent(60) - .withType(experienceTypes.getFirst()) - .withComment("Comment test 3") - .withStartDate(LocalDate.of(2023, 7, 16)) - .withEndDate(LocalDate.of(2024, 7, 15)) - .build()); - - return experiences; + return TestData.EXPERIENCES; } } \ No newline at end of file diff --git a/backend/src/test/java/ch/puzzle/pcts/service/persistence/ExperienceTypePersistenceServiceIT.java b/backend/src/test/java/ch/puzzle/pcts/service/persistence/ExperienceTypePersistenceServiceIT.java index b79899517..a47ec43b7 100644 --- a/backend/src/test/java/ch/puzzle/pcts/service/persistence/ExperienceTypePersistenceServiceIT.java +++ b/backend/src/test/java/ch/puzzle/pcts/service/persistence/ExperienceTypePersistenceServiceIT.java @@ -2,6 +2,7 @@ import ch.puzzle.pcts.model.experiencetype.ExperienceType; import ch.puzzle.pcts.repository.ExperienceTypeRepository; +import ch.puzzle.pcts.util.TestData; import java.math.BigDecimal; import java.util.List; import org.springframework.beans.factory.annotation.Autowired; @@ -26,16 +27,6 @@ ExperienceType getModel() { @Override List getAll() { - return List - .of(new ExperienceType(1L, - "ExperienceType 1", - BigDecimal.valueOf(0), - BigDecimal.valueOf(12), - BigDecimal.valueOf(4.005)), - new ExperienceType(2L, - "ExperienceType 2", - BigDecimal.valueOf(12), - BigDecimal.valueOf(10.7989), - BigDecimal.valueOf(6))); + return TestData.EXPERIENCE_TYPES; } } diff --git a/backend/src/test/java/ch/puzzle/pcts/service/persistence/LeadershipTypePersistenceServiceIT.java b/backend/src/test/java/ch/puzzle/pcts/service/persistence/LeadershipTypePersistenceServiceIT.java index 61f7dbd6a..f829326a3 100644 --- a/backend/src/test/java/ch/puzzle/pcts/service/persistence/LeadershipTypePersistenceServiceIT.java +++ b/backend/src/test/java/ch/puzzle/pcts/service/persistence/LeadershipTypePersistenceServiceIT.java @@ -12,6 +12,7 @@ import ch.puzzle.pcts.model.certificatetype.CertificateType; import ch.puzzle.pcts.model.certificatetype.Tag; import ch.puzzle.pcts.repository.CertificateTypeRepository; +import ch.puzzle.pcts.util.TestData; import java.math.BigDecimal; import java.util.List; import java.util.Map; @@ -42,45 +43,7 @@ CertificateType getModel() { @Override List getAll() { - return List - .of(new CertificateType(1L, - "Certificate Type 1", - BigDecimal.valueOf(5.5), - "This is Certificate 1", - Set.of(new Tag(1L, "Tag 1"))), - new CertificateType(2L, - "Certificate Type 2", - BigDecimal.valueOf(1), - "This is Certificate 2", - Set.of(new Tag(2L, "Longer tag name"))), - new CertificateType(3L, - "Certificate Type 3", - BigDecimal.valueOf(3), - "This is Certificate 3", - Set.of()), - new CertificateType(4L, - "Certificate Type 4", - BigDecimal.valueOf(0.5), - "This is Certificate 4", - Set.of()), - new CertificateType(5L, - "LeadershipExperience Type 1", - BigDecimal.valueOf(5.5), - "This is LeadershipExperience 1", - Set.of(), - CertificateKind.MILITARY_FUNCTION), - new CertificateType(6L, - "LeadershipExperience Type 2", - BigDecimal.valueOf(1), - "This is LeadershipExperience 2", - Set.of(), - CertificateKind.YOUTH_AND_SPORT), - new CertificateType(7L, - "LeadershipExperience Type 3", - BigDecimal.valueOf(3), - "This is LeadershipExperience 3", - Set.of(), - CertificateKind.LEADERSHIP_TRAINING)); + return TestData.LEADERSHIP_EXPERIENCE_TYPES; } @Override diff --git a/backend/src/test/java/ch/puzzle/pcts/service/persistence/MemberPersistenceServiceIT.java b/backend/src/test/java/ch/puzzle/pcts/service/persistence/MemberPersistenceServiceIT.java index bf6068a3f..63962d25c 100644 --- a/backend/src/test/java/ch/puzzle/pcts/service/persistence/MemberPersistenceServiceIT.java +++ b/backend/src/test/java/ch/puzzle/pcts/service/persistence/MemberPersistenceServiceIT.java @@ -2,12 +2,9 @@ import ch.puzzle.pcts.model.member.EmploymentState; import ch.puzzle.pcts.model.member.Member; -import ch.puzzle.pcts.model.organisationunit.OrganisationUnit; import ch.puzzle.pcts.repository.MemberRepository; -import java.time.Instant; +import ch.puzzle.pcts.util.TestData; import java.time.LocalDate; -import java.time.LocalDateTime; -import java.time.ZoneOffset; import java.util.List; import org.springframework.beans.factory.annotation.Autowired; @@ -29,37 +26,12 @@ Member getModel() { .withAbbreviation("M1") .withDateOfHire(LocalDate.of(2019, 8, 4)) .withBirthDate(LocalDate.of(1970, 1, 1)) - .withOrganisationUnit(new OrganisationUnit(2L, "OrganisationUnit 2")) + .withOrganisationUnit(TestData.ORG_UNIT_2) .build(); } @Override List getAll() { - OrganisationUnit deletedOrganisationUnit = new OrganisationUnit(1L, "OrganisationUnit 1"); - deletedOrganisationUnit.setDeletedAt(LocalDateTime.ofInstant(Instant.EPOCH, ZoneOffset.UTC)); - - return List - .of(Member.Builder - .builder() - .withId(1L) - .withFirstName("Member 1") - .withLastName("Test") - .withEmploymentState(EmploymentState.MEMBER) - .withAbbreviation("M1") - .withDateOfHire(LocalDate.of(2021, 7, 15)) - .withBirthDate(LocalDate.of(1999, 8, 10)) - .withOrganisationUnit(deletedOrganisationUnit) - .build(), - Member.Builder - .builder() - .withId(2L) - .withFirstName("Member 2") - .withLastName("Test") - .withEmploymentState(EmploymentState.MEMBER) - .withAbbreviation("M2") - .withDateOfHire(LocalDate.of(2020, 6, 1)) - .withBirthDate(LocalDate.of(1998, 3, 3)) - .withOrganisationUnit(new OrganisationUnit(2L, "OrganisationUnit 2")) - .build()); + return TestData.MEMBERS; } } diff --git a/backend/src/test/java/ch/puzzle/pcts/service/persistence/OrganisationUnitPersistenceServiceIT.java b/backend/src/test/java/ch/puzzle/pcts/service/persistence/OrganisationUnitPersistenceServiceIT.java index 96ee30c40..f3581ba31 100644 --- a/backend/src/test/java/ch/puzzle/pcts/service/persistence/OrganisationUnitPersistenceServiceIT.java +++ b/backend/src/test/java/ch/puzzle/pcts/service/persistence/OrganisationUnitPersistenceServiceIT.java @@ -4,6 +4,7 @@ import ch.puzzle.pcts.model.organisationunit.OrganisationUnit; import ch.puzzle.pcts.repository.OrganisationUnitRepository; +import ch.puzzle.pcts.util.TestData; import java.util.List; import java.util.Optional; import org.junit.jupiter.api.DisplayName; @@ -29,7 +30,7 @@ OrganisationUnit getModel() { @Override List getAll() { - return List.of(new OrganisationUnit(2L, "OrganisationUnit 2")); + return TestData.ORGANISATION_UNITS; } @DisplayName("Should get organisationUnit by name") diff --git a/backend/src/test/java/ch/puzzle/pcts/service/persistence/RolePersistenceServiceIT.java b/backend/src/test/java/ch/puzzle/pcts/service/persistence/RolePersistenceServiceIT.java index 95c2a2c12..20bec7d48 100644 --- a/backend/src/test/java/ch/puzzle/pcts/service/persistence/RolePersistenceServiceIT.java +++ b/backend/src/test/java/ch/puzzle/pcts/service/persistence/RolePersistenceServiceIT.java @@ -4,6 +4,7 @@ import ch.puzzle.pcts.model.role.Role; import ch.puzzle.pcts.repository.RoleRepository; +import ch.puzzle.pcts.util.TestData; import java.util.List; import java.util.Optional; import org.junit.jupiter.api.DisplayName; @@ -26,7 +27,7 @@ Role getModel() { } List getAll() { - return List.of(new Role(2L, "Role 2", false)); + return TestData.ROLES; } @DisplayName("Should get role by name") diff --git a/backend/src/test/java/ch/puzzle/pcts/service/persistence/TagPersistenceServiceIT.java b/backend/src/test/java/ch/puzzle/pcts/service/persistence/TagPersistenceServiceIT.java index d9b54868e..9785c59e0 100644 --- a/backend/src/test/java/ch/puzzle/pcts/service/persistence/TagPersistenceServiceIT.java +++ b/backend/src/test/java/ch/puzzle/pcts/service/persistence/TagPersistenceServiceIT.java @@ -4,6 +4,7 @@ import ch.puzzle.pcts.model.certificatetype.Tag; import ch.puzzle.pcts.repository.TagRepository; +import ch.puzzle.pcts.util.TestData; import jakarta.transaction.Transactional; import java.util.List; import org.junit.jupiter.api.DisplayName; @@ -27,7 +28,7 @@ Tag getModel() { } List getAll() { - return List.of(new Tag(1L, "Tag 1"), new Tag(2L, "Longer tag name")); + return TestData.TAGS; } @DisplayName("Should find tag by name written in different cases") diff --git a/backend/src/test/java/ch/puzzle/pcts/util/TestData.java b/backend/src/test/java/ch/puzzle/pcts/util/TestData.java new file mode 100644 index 000000000..07a7618ae --- /dev/null +++ b/backend/src/test/java/ch/puzzle/pcts/util/TestData.java @@ -0,0 +1,278 @@ +package ch.puzzle.pcts.util; + +import ch.puzzle.pcts.model.calculation.Calculation; +import ch.puzzle.pcts.model.calculation.CalculationState; +import ch.puzzle.pcts.model.certificate.Certificate; +import ch.puzzle.pcts.model.certificatetype.CertificateKind; +import ch.puzzle.pcts.model.certificatetype.CertificateType; +import ch.puzzle.pcts.model.certificatetype.Tag; +import ch.puzzle.pcts.model.degree.Degree; +import ch.puzzle.pcts.model.degreetype.DegreeType; +import ch.puzzle.pcts.model.experience.Experience; +import ch.puzzle.pcts.model.experiencetype.ExperienceType; +import ch.puzzle.pcts.model.member.EmploymentState; +import ch.puzzle.pcts.model.member.Member; +import ch.puzzle.pcts.model.organisationunit.OrganisationUnit; +import ch.puzzle.pcts.model.role.Role; +import java.math.BigDecimal; +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.util.List; +import java.util.Set; + +public class TestData { + + private static final LocalDateTime UNIX_EPOCH = LocalDateTime.of(1970, 1, 1, 0, 0); + + private TestData() { + } + + public static final OrganisationUnit ORG_UNIT_1; + static { + ORG_UNIT_1 = new OrganisationUnit(1L, "OrganisationUnit 1"); + ORG_UNIT_1.setDeletedAt(UNIX_EPOCH); + } + + public static final OrganisationUnit ORG_UNIT_2 = new OrganisationUnit(2L, "OrganisationUnit 2"); + + public static final Role ROLE_2 = new Role(2L, "Role 2", false); + + public static final Tag TAG_1 = new Tag(1L, "Tag 1"); + public static final Tag TAG_2 = new Tag(2L, "Longer tag name"); + + public static final Member MEMBER_1 = Member.Builder + .builder() + .withId(1L) + .withFirstName("Member 1") + .withLastName("Test") + .withEmploymentState(EmploymentState.MEMBER) + .withAbbreviation("M1") + .withDateOfHire(LocalDate.of(2021, 7, 15)) + .withBirthDate(LocalDate.of(1999, 8, 10)) + .withOrganisationUnit(ORG_UNIT_1) + .build(); + + public static final Member MEMBER_2 = Member.Builder + .builder() + .withId(2L) + .withFirstName("Member 2") + .withLastName("Test") + .withEmploymentState(EmploymentState.MEMBER) + .withAbbreviation("M2") + .withDateOfHire(LocalDate.of(2020, 6, 1)) + .withBirthDate(LocalDate.of(1998, 3, 3)) + .withOrganisationUnit(ORG_UNIT_2) + .build(); + + public static final CertificateType CERT_TYPE_1 = new CertificateType(1L, + "Certificate Type 1", + BigDecimal.valueOf(5.5), + "This is Certificate 1", + Set.of(TAG_1), + CertificateKind.CERTIFICATE); + + public static final CertificateType CERT_TYPE_2 = new CertificateType(2L, + "Certificate Type 2", + BigDecimal.valueOf(1), + "This is Certificate 2", + Set.of(TAG_2), + CertificateKind.CERTIFICATE); + + public static final CertificateType CERT_TYPE_3 = new CertificateType(3L, + "Certificate Type 3", + BigDecimal.valueOf(3), + "This is Certificate 3", + Set.of(), + CertificateKind.CERTIFICATE); + + public static final CertificateType CERT_TYPE_4 = new CertificateType(4L, + "Certificate Type 4", + BigDecimal.valueOf(0.5), + "This is Certificate 4", + Set.of(), + CertificateKind.CERTIFICATE); + + public static final CertificateType LEADERSHIP_TYPE_1 = new CertificateType(5L, + "LeadershipExperience Type 1", + BigDecimal.valueOf(5.5), + "This is LeadershipExperience 1", + Set.of(), + CertificateKind.MILITARY_FUNCTION); + + public static final CertificateType LEADERSHIP_TYPE_2 = new CertificateType(6L, + "LeadershipExperience Type 2", + BigDecimal.valueOf(1), + "This is LeadershipExperience 2", + Set.of(), + CertificateKind.YOUTH_AND_SPORT); + + public static final CertificateType LEADERSHIP_TYPE_3 = new CertificateType(7L, + "LeadershipExperience Type 3", + BigDecimal.valueOf(3), + "This is LeadershipExperience 3", + Set.of(), + CertificateKind.LEADERSHIP_TRAINING); + + public static final Certificate CERTIFICATE_1 = Certificate.Builder + .builder() + .withId(1L) + .withMember(MEMBER_1) + .withCertificateType(CERT_TYPE_1) + .withCompletedAt(LocalDate.of(2023, 1, 15)) + .withValidUntil(LocalDate.of(2025, 1, 14)) + .withComment("Completed first aid training.") + .build(); + + public static final Certificate CERTIFICATE_2 = Certificate.Builder + .builder() + .withId(2L) + .withMember(MEMBER_2) + .withCertificateType(CERT_TYPE_2) + .withCompletedAt(LocalDate.of(2022, 11, 1)) + .withValidUntil(null) + .withComment("Completed first aid training.") + .build(); + + public static final Certificate CERTIFICATE_3 = Certificate.Builder + .builder() + .withId(3L) + .withMember(MEMBER_2) + .withCertificateType(CERT_TYPE_1) + .withCompletedAt(LocalDate.of(2023, 1, 15)) + .withValidUntil(LocalDate.of(2025, 1, 14)) + .withComment(null) + .build(); + + public static final Certificate CERTIFICATE_4 = Certificate.Builder + .builder() + .withId(4L) + .withMember(MEMBER_1) + .withCertificateType(CERT_TYPE_2) + .withCompletedAt(LocalDate.of(2010, 8, 12)) + .withValidUntil(LocalDate.of(2023, 3, 25)) + .withComment("Left organization.") + .build(); + + public static final DegreeType DEGREE_TYPE_1 = new DegreeType(1L, + "Degree type 1", + BigDecimal.valueOf(120.55), + BigDecimal.valueOf(60), + BigDecimal.valueOf(15.45)); + + public static final DegreeType DEGREE_TYPE_2 = new DegreeType(2L, + "Degree type 2", + BigDecimal.valueOf(12), + BigDecimal.valueOf(3.961), + BigDecimal.valueOf(3)); + + public static final Degree DEGREE_1 = Degree.Builder + .builder() + .withId(1L) + .withMember(MEMBER_1) + .withName("Degree 1") + .withInstitution("Institution") + .withCompleted(true) + .withDegreeType(DEGREE_TYPE_1) + .withStartDate(LocalDate.of(2015, 9, 1)) + .withEndDate(LocalDate.of(2020, 6, 1)) + .withComment("Comment") + .build(); + + public static final Degree DEGREE_2 = Degree.Builder + .builder() + .withId(2L) + .withMember(MEMBER_2) + .withName("Degree 2") + .withInstitution("Institution") + .withCompleted(false) + .withDegreeType(DEGREE_TYPE_2) + .withStartDate(LocalDate.of(2016, 9, 1)) + .withEndDate(LocalDate.of(2019, 6, 30)) + .withComment("Comment") + .build(); + + public static final ExperienceType EXP_TYPE_1 = new ExperienceType(1L, + "ExperienceType 1", + BigDecimal.ZERO, + BigDecimal.valueOf(12), + BigDecimal.valueOf(4.005)); + + public static final ExperienceType EXP_TYPE_2 = new ExperienceType(2L, + "ExperienceType 2", + BigDecimal.valueOf(12), + BigDecimal.valueOf(10.7989), + BigDecimal.valueOf(6)); + + public static final Experience EXPERIENCE_1 = new Experience.Builder() + .withId(2L) + .withMember(MEMBER_1) + .withName("Experience 2") + .withEmployer("Employer 2") + .withPercent(80) + .withType(EXP_TYPE_2) + .withComment("Comment test 2") + .withStartDate(LocalDate.of(2022, 7, 16)) + .withEndDate(LocalDate.of(2023, 7, 15)) + .build(); + + public static final Experience EXPERIENCE_2 = new Experience.Builder() + .withId(3L) + .withMember(MEMBER_2) + .withName("Experience 3") + .withEmployer("Employer 3") + .withPercent(60) + .withType(EXP_TYPE_1) + .withComment("Comment test 3") + .withStartDate(LocalDate.of(2023, 7, 16)) + .withEndDate(LocalDate.of(2024, 7, 15)) + .build(); + + public static final Calculation CALCULATION_1 = new Calculation(1L, + MEMBER_1, + ROLE_2, + CalculationState.DRAFT, + LocalDate.of(2025, 1, 14), + "Ldap User"); + + public static final Calculation CALCULATION_2 = new Calculation(2L, + MEMBER_2, + ROLE_2, + CalculationState.ARCHIVED, + LocalDate.of(2025, 1, 14), + "Ldap User 2"); + + public static final Calculation CALCULATION_3 = new Calculation(3L, + MEMBER_2, + ROLE_2, + CalculationState.ACTIVE, + null, + null); + + public static final List ORGANISATION_UNITS = List.of(ORG_UNIT_2); + + public static final List ROLES = List.of(ROLE_2); + + public static final List TAGS = List.of(TAG_1, TAG_2); + + public static final List MEMBERS = List.of(MEMBER_1, MEMBER_2); + + public static final List CERTIFICATE_TYPES = List + .of(CERT_TYPE_1, CERT_TYPE_2, CERT_TYPE_3, CERT_TYPE_4); + + public static final List LEADERSHIP_EXPERIENCE_TYPES = List + .of(LEADERSHIP_TYPE_1, LEADERSHIP_TYPE_2, LEADERSHIP_TYPE_3); + + public static final List CERTIFICATES = List + .of(CERTIFICATE_1, CERTIFICATE_2, CERTIFICATE_3, CERTIFICATE_4); + + public static final List DEGREE_TYPES = List.of(DEGREE_TYPE_1, DEGREE_TYPE_2); + + public static final List DEGREES = List.of(DEGREE_1, DEGREE_2); + + public static final List EXPERIENCE_TYPES = List.of(EXP_TYPE_1, EXP_TYPE_2); + + public static final List EXPERIENCES = List.of(EXPERIENCE_1, EXPERIENCE_2); + + public static final List CALCULATIONS = List.of(CALCULATION_1, CALCULATION_2, CALCULATION_3); + +} From f927a86f858f3d2f2720c779953ac6cebeb5647c Mon Sep 17 00:00:00 2001 From: Nevio Di Gennaro Date: Tue, 23 Dec 2025 09:20:53 +0100 Subject: [PATCH 12/30] test: add tests for mapper and controller #50 --- .../pcts/mapper/MemberOverviewMapper.java | 4 +- .../model/memberoverview/MemberOverview.java | 4 + .../MemberOverviewBusinessService.java | 8 +- .../MemberOverviewValidationService.java | 8 + .../MemberOverviewControllerIT.java | 74 ++++++ .../pcts/mapper/MemberOverviewMapperTest.java | 70 ++++++ .../MemberOverviewBusinessServiceTest.java | 45 ++++ .../MemberOverviewPersistenceServiceIT.java | 51 ++++ .../MemberOverviewValidationServiceTest.java | 39 +++ .../java/ch/puzzle/pcts/util/TestData.java | 229 +++++++++++++++++- 10 files changed, 526 insertions(+), 6 deletions(-) create mode 100644 backend/src/main/java/ch/puzzle/pcts/service/validation/MemberOverviewValidationService.java create mode 100644 backend/src/test/java/ch/puzzle/pcts/controller/MemberOverviewControllerIT.java create mode 100644 backend/src/test/java/ch/puzzle/pcts/mapper/MemberOverviewMapperTest.java create mode 100644 backend/src/test/java/ch/puzzle/pcts/service/business/MemberOverviewBusinessServiceTest.java create mode 100644 backend/src/test/java/ch/puzzle/pcts/service/persistence/MemberOverviewPersistenceServiceIT.java create mode 100644 backend/src/test/java/ch/puzzle/pcts/service/validation/MemberOverviewValidationServiceTest.java diff --git a/backend/src/main/java/ch/puzzle/pcts/mapper/MemberOverviewMapper.java b/backend/src/main/java/ch/puzzle/pcts/mapper/MemberOverviewMapper.java index e7dfd403f..96cfa7eb8 100644 --- a/backend/src/main/java/ch/puzzle/pcts/mapper/MemberOverviewMapper.java +++ b/backend/src/main/java/ch/puzzle/pcts/mapper/MemberOverviewMapper.java @@ -91,7 +91,7 @@ public MemberOverviewDto toDto(List entities) { }) .toList(); - List LeadershipExperiences = entities + List leadershipExperiences = entities .stream() .filter(e -> e.getCertificateId() != null && !CertificateKind.CERTIFICATE.equals(e.getLeadershipTypeKind())) @@ -111,7 +111,7 @@ public MemberOverviewDto toDto(List entities) { }) .toList(); - MemberCvDto cvDto = new MemberCvDto(degrees, experiences, certificates, LeadershipExperiences); + MemberCvDto cvDto = new MemberCvDto(degrees, experiences, certificates, leadershipExperiences); return new MemberOverviewDto(memberDto, cvDto); } diff --git a/backend/src/main/java/ch/puzzle/pcts/model/memberoverview/MemberOverview.java b/backend/src/main/java/ch/puzzle/pcts/model/memberoverview/MemberOverview.java index 8df079259..ca1ede9e2 100644 --- a/backend/src/main/java/ch/puzzle/pcts/model/memberoverview/MemberOverview.java +++ b/backend/src/main/java/ch/puzzle/pcts/model/memberoverview/MemberOverview.java @@ -529,5 +529,9 @@ public Builder withExperienceTypeName(String experienceTypeName) { this.experienceTypeName = trim(experienceTypeName); return this; } + + public MemberOverview build() { + return new MemberOverview(this); + } } } \ No newline at end of file diff --git a/backend/src/main/java/ch/puzzle/pcts/service/business/MemberOverviewBusinessService.java b/backend/src/main/java/ch/puzzle/pcts/service/business/MemberOverviewBusinessService.java index 4c25ea89e..fb3907f8e 100644 --- a/backend/src/main/java/ch/puzzle/pcts/service/business/MemberOverviewBusinessService.java +++ b/backend/src/main/java/ch/puzzle/pcts/service/business/MemberOverviewBusinessService.java @@ -4,6 +4,7 @@ import ch.puzzle.pcts.model.memberoverview.MemberOverview; import ch.puzzle.pcts.service.persistence.MemberOverviewPersistenceService; +import ch.puzzle.pcts.service.validation.MemberOverviewValidationService; import java.util.List; import org.springframework.stereotype.Service; @@ -12,11 +13,16 @@ public class MemberOverviewBusinessService { private final MemberOverviewPersistenceService persistenceService; - public MemberOverviewBusinessService(MemberOverviewPersistenceService persistenceService) { + private final MemberOverviewValidationService validationService; + + public MemberOverviewBusinessService(MemberOverviewPersistenceService persistenceService, + MemberOverviewValidationService validationService) { this.persistenceService = persistenceService; + this.validationService = validationService; } public List getById(Long id) { + validationService.validateOnGetById(id); return persistenceService.getById(id); } diff --git a/backend/src/main/java/ch/puzzle/pcts/service/validation/MemberOverviewValidationService.java b/backend/src/main/java/ch/puzzle/pcts/service/validation/MemberOverviewValidationService.java new file mode 100644 index 000000000..cc3fa7aa2 --- /dev/null +++ b/backend/src/main/java/ch/puzzle/pcts/service/validation/MemberOverviewValidationService.java @@ -0,0 +1,8 @@ +package ch.puzzle.pcts.service.validation; + +import ch.puzzle.pcts.model.memberoverview.MemberOverview; +import org.springframework.stereotype.Service; + +@Service +public class MemberOverviewValidationService extends ValidationBase { +} diff --git a/backend/src/test/java/ch/puzzle/pcts/controller/MemberOverviewControllerIT.java b/backend/src/test/java/ch/puzzle/pcts/controller/MemberOverviewControllerIT.java new file mode 100644 index 000000000..078a68f8a --- /dev/null +++ b/backend/src/test/java/ch/puzzle/pcts/controller/MemberOverviewControllerIT.java @@ -0,0 +1,74 @@ +package ch.puzzle.pcts.controller; + +import static org.mockito.ArgumentMatchers.anyLong; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +import ch.puzzle.pcts.SpringSecurityConfig; +import ch.puzzle.pcts.dto.memberoverview.MemberOverviewDto; +import ch.puzzle.pcts.mapper.MemberOverviewMapper; +import ch.puzzle.pcts.model.memberoverview.MemberOverview; +import ch.puzzle.pcts.service.business.MemberOverviewBusinessService; +import ch.puzzle.pcts.util.JsonDtoMatcher; +import ch.puzzle.pcts.util.TestData; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.SerializationFeature; +import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; +import java.util.List; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.BDDMockito; +import org.mockito.junit.jupiter.MockitoExtension; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest; +import org.springframework.context.annotation.Import; +import org.springframework.http.MediaType; +import org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors; +import org.springframework.test.context.bean.override.mockito.MockitoBean; +import org.springframework.test.web.servlet.MockMvc; + +@Import(SpringSecurityConfig.class) +@ExtendWith(MockitoExtension.class) +@WebMvcTest(MemberOverviewController.class) +class MemberOverviewControllerIT { + + @MockitoBean + private MemberOverviewBusinessService service; + + @MockitoBean + private MemberOverviewMapper mapper; + + @Autowired + private MockMvc mvc; + + private static final String BASEURL = "/api/v1/member-overviews"; + + private final ObjectMapper objectMapper = new ObjectMapper() + .registerModule(new JavaTimeModule()) + .disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS); + + @DisplayName("Should successfully get member overview by member id") + @Test + void shouldGetMemberOverviewById() throws Exception { + Long memberId = 1L; + + List memberOverviews = TestData.MEMBER_1_OVERVIEWS; + MemberOverviewDto expectedDto = TestData.MEMBER_1_OVERVIEW_DTO; + + BDDMockito.given(service.getById(anyLong())).willReturn(memberOverviews); + BDDMockito.given(mapper.toDto(memberOverviews)).willReturn(expectedDto); + + mvc + .perform(get(BASEURL + "/" + memberId) + .with(SecurityMockMvcRequestPostProcessors.csrf()) + .accept(MediaType.APPLICATION_JSON)) + .andExpect(status().isOk()) + .andExpect(JsonDtoMatcher.matchesDto(expectedDto, "$")); + + verify(service, times(1)).getById(memberId); + verify(mapper, times(1)).toDto(memberOverviews); + } +} diff --git a/backend/src/test/java/ch/puzzle/pcts/mapper/MemberOverviewMapperTest.java b/backend/src/test/java/ch/puzzle/pcts/mapper/MemberOverviewMapperTest.java new file mode 100644 index 000000000..d4af202a5 --- /dev/null +++ b/backend/src/test/java/ch/puzzle/pcts/mapper/MemberOverviewMapperTest.java @@ -0,0 +1,70 @@ +package ch.puzzle.pcts.mapper; + +import ch.puzzle.pcts.dto.memberoverview.MemberCvDto; +import ch.puzzle.pcts.dto.memberoverview.MemberOverviewDto; +import ch.puzzle.pcts.dto.memberoverview.MemberOverviewMemberDto; +import ch.puzzle.pcts.dto.memberoverview.certificate.MemberOverviewCertificateDto; +import ch.puzzle.pcts.dto.memberoverview.degree.MemberOverviewDegreeDto; +import ch.puzzle.pcts.dto.memberoverview.experience.MemberOverviewExperienceDto; +import ch.puzzle.pcts.model.member.EmploymentState; +import ch.puzzle.pcts.util.TestData; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.InjectMocks; +import org.mockito.junit.jupiter.MockitoExtension; + +import java.time.LocalDate; + +import static org.assertj.core.api.Assertions.assertThat; + + +@ExtendWith(MockitoExtension.class) +class MemberOverviewMapperTest { + + @InjectMocks + private MemberOverviewMapper mapper; + + @DisplayName("Should map List to MemberOverviewDto") + @Test + void shouldReturnMemberOverviewDto() { + MemberOverviewDto dto = mapper.toDto(TestData.MEMBER_1_OVERVIEWS); + + assertThat(dto).isNotNull(); + assertThat(dto.member()).isNotNull(); + assertThat(dto.cv()).isNotNull(); + + MemberOverviewMemberDto member = dto.member(); + assertThat(member.id()).isEqualTo(1L); + assertThat(member.firstName()).isEqualTo("Member 1"); + assertThat(member.lastName()).isEqualTo("Test"); + assertThat(member.abbreviation()).isEqualTo("M1"); + assertThat(member.employmentState()).isEqualTo(EmploymentState.MEMBER); + assertThat(member.dateOfHire()).isEqualTo(LocalDate.of(2021, 7, 15)); + assertThat(member.birthDate()).isEqualTo(LocalDate.of(1999, 8, 10)); + assertThat(member.organisationUnitName()).isEqualTo("OrganisationUnit 1"); + + MemberCvDto cv = dto.cv(); + + assertThat(cv.degrees()).hasSize(1); + MemberOverviewDegreeDto degree = cv.degrees().getFirst(); + assertThat(degree.id()).isEqualTo(1L); + assertThat(degree.name()).isEqualTo("Degree 1"); + assertThat(degree.type().name()).isEqualTo("Degree type 1"); + assertThat(degree.startDate()).isEqualTo(LocalDate.of(2015, 9, 1)); + assertThat(degree.endDate()).isEqualTo(LocalDate.of(2020, 6, 1)); + assertThat(degree.comment()).isEqualTo("Comment"); + + assertThat(cv.experiences()).hasSize(2); + assertThat(cv.experiences()) + .extracting(MemberOverviewExperienceDto::id) + .containsExactlyInAnyOrder(1L, 2L); + + assertThat(cv.certificates()).hasSize(2); + assertThat(cv.certificates()) + .extracting(MemberOverviewCertificateDto::id) + .containsExactlyInAnyOrder(1L, 4L); + + assertThat(cv.leadershipExperiences()).isEmpty(); + } +} diff --git a/backend/src/test/java/ch/puzzle/pcts/service/business/MemberOverviewBusinessServiceTest.java b/backend/src/test/java/ch/puzzle/pcts/service/business/MemberOverviewBusinessServiceTest.java new file mode 100644 index 000000000..7de4e38ef --- /dev/null +++ b/backend/src/test/java/ch/puzzle/pcts/service/business/MemberOverviewBusinessServiceTest.java @@ -0,0 +1,45 @@ +package ch.puzzle.pcts.service.business; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import ch.puzzle.pcts.model.memberoverview.MemberOverview; +import ch.puzzle.pcts.service.persistence.MemberOverviewPersistenceService; +import ch.puzzle.pcts.service.validation.MemberOverviewValidationService; +import java.util.List; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; + +@ExtendWith(MockitoExtension.class) +class MemberOverviewBusinessServiceTest { + + @Mock + private MemberOverviewValidationService validationService; + + @Mock + private MemberOverviewPersistenceService persistenceService; + + @Mock + private List memberOverviews; + + @InjectMocks + private MemberOverviewBusinessService businessService; + + @DisplayName("Should get organisationUnit by id") + @Test + void shouldGetById() { + Long id = 1L; + when(persistenceService.getById(id)).thenReturn(memberOverviews); + + List result = businessService.getById(id); + + assertEquals(memberOverviews, result); + verify(persistenceService).getById(id); + verify(validationService).validateOnGetById(id); + } +} diff --git a/backend/src/test/java/ch/puzzle/pcts/service/persistence/MemberOverviewPersistenceServiceIT.java b/backend/src/test/java/ch/puzzle/pcts/service/persistence/MemberOverviewPersistenceServiceIT.java new file mode 100644 index 000000000..907eab245 --- /dev/null +++ b/backend/src/test/java/ch/puzzle/pcts/service/persistence/MemberOverviewPersistenceServiceIT.java @@ -0,0 +1,51 @@ +package ch.puzzle.pcts.service.persistence; + +import static org.assertj.core.api.Assertions.assertThat; + +import ch.puzzle.pcts.model.memberoverview.MemberOverview; +import ch.puzzle.pcts.util.TestData; +import java.util.List; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; + +class MemberOverviewPersistenceServiceIT extends PersistenceCoreIT { + + protected final MemberOverviewPersistenceService service; + + @Autowired + MemberOverviewPersistenceServiceIT(MemberOverviewPersistenceService service) { + this.service = service; + } + + @DisplayName("Should get all member overview with member id 1") + @Test + void shouldGetAllMemberOverviewRowsForMember1() { + List memberOverviews = service.getById(1L); + + assertThat(memberOverviews).isNotNull(); + assertThat(memberOverviews).hasSize(4); + + assertThat(memberOverviews).allSatisfy(row -> { + assertThat(row.getMemberId()).isEqualTo(1L); + assertThat(row.getFirstName()).isEqualTo("Member 1"); + assertThat(row.getLastName()).isEqualTo("Test"); + assertThat(row.getAbbreviation()).isEqualTo("M1"); + assertThat(row.getOrganisationUnitName()).isEqualTo("OrganisationUnit 1"); + }); + + assertThat(memberOverviews) + .extracting(MemberOverview::getCertificateId) + .containsExactlyInAnyOrder(1L, 1L, 4L, 4L); + + assertThat(memberOverviews).extracting(MemberOverview::getDegreeId).containsOnly(1L); + + assertThat(memberOverviews) + .extracting(MemberOverview::getExperienceId) + .containsExactlyInAnyOrder(1L, 2L, 1L, 2L); + + assertThat(memberOverviews) + .usingRecursiveFieldByFieldElementComparator() + .containsExactlyElementsOf(TestData.MEMBER_1_OVERVIEWS); + } +} diff --git a/backend/src/test/java/ch/puzzle/pcts/service/validation/MemberOverviewValidationServiceTest.java b/backend/src/test/java/ch/puzzle/pcts/service/validation/MemberOverviewValidationServiceTest.java new file mode 100644 index 000000000..90fd619d3 --- /dev/null +++ b/backend/src/test/java/ch/puzzle/pcts/service/validation/MemberOverviewValidationServiceTest.java @@ -0,0 +1,39 @@ +package ch.puzzle.pcts.service.validation; + +import static org.junit.jupiter.api.Assertions.*; + +import ch.puzzle.pcts.dto.error.ErrorKey; +import ch.puzzle.pcts.dto.error.FieldKey; +import ch.puzzle.pcts.exception.PCTSException; +import java.util.List; +import java.util.Map; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.InjectMocks; +import org.mockito.junit.jupiter.MockitoExtension; + +@ExtendWith(MockitoExtension.class) +class MemberOverviewValidationServiceTest { + + @InjectMocks + MemberOverviewValidationService validationService; + + @DisplayName("Should be successful validateOnGet() when Id is valid") + @Test + void validateOnGetByIdShouldBeSuccessfulWhenIdIsValid() { + Long id = 1L; + assertDoesNotThrow(() -> validationService.validateOnGetById(id)); + } + + @DisplayName("Should throw exception validateOnGet() when Id is null") + @Test + void validateOnGetByIdShouldThrowExceptionWhenIdIsNull() { + Long id = null; + + PCTSException exception = assertThrows(PCTSException.class, () -> validationService.validateOnGetById(id)); + + assertEquals(List.of(ErrorKey.VALIDATION), exception.getErrorKeys()); + assertEquals(List.of(Map.of(FieldKey.FIELD, "id")), exception.getErrorAttributes()); + } +} diff --git a/backend/src/test/java/ch/puzzle/pcts/util/TestData.java b/backend/src/test/java/ch/puzzle/pcts/util/TestData.java index 07a7618ae..77cc3bb45 100644 --- a/backend/src/test/java/ch/puzzle/pcts/util/TestData.java +++ b/backend/src/test/java/ch/puzzle/pcts/util/TestData.java @@ -1,5 +1,14 @@ package ch.puzzle.pcts.util; +import ch.puzzle.pcts.dto.memberoverview.MemberCvDto; +import ch.puzzle.pcts.dto.memberoverview.MemberOverviewDto; +import ch.puzzle.pcts.dto.memberoverview.MemberOverviewMemberDto; +import ch.puzzle.pcts.dto.memberoverview.certificate.MemberOverviewCertificateDto; +import ch.puzzle.pcts.dto.memberoverview.certificate.MemberOverviewCertificateTypeDto; +import ch.puzzle.pcts.dto.memberoverview.degree.MemberOverviewDegreeDto; +import ch.puzzle.pcts.dto.memberoverview.degree.MemberOverviewDegreeTypeDto; +import ch.puzzle.pcts.dto.memberoverview.experience.MemberOverviewExperienceDto; +import ch.puzzle.pcts.dto.memberoverview.experience.MemberOverviewExperienceTypeDto; import ch.puzzle.pcts.model.calculation.Calculation; import ch.puzzle.pcts.model.calculation.CalculationState; import ch.puzzle.pcts.model.certificate.Certificate; @@ -12,6 +21,7 @@ import ch.puzzle.pcts.model.experiencetype.ExperienceType; import ch.puzzle.pcts.model.member.EmploymentState; import ch.puzzle.pcts.model.member.Member; +import ch.puzzle.pcts.model.memberoverview.MemberOverview; import ch.puzzle.pcts.model.organisationunit.OrganisationUnit; import ch.puzzle.pcts.model.role.Role; import java.math.BigDecimal; @@ -202,8 +212,23 @@ private TestData() { BigDecimal.valueOf(12), BigDecimal.valueOf(10.7989), BigDecimal.valueOf(6)); + private static final Experience EXPERIENCE_1; + static { + EXPERIENCE_1 = new Experience.Builder() + .withId(1L) + .withMember(MEMBER_1) + .withName("Experience 1") + .withEmployer("Employer 1") + .withPercent(100) + .withType(EXP_TYPE_1) + .withComment("Comment test 1") + .withStartDate(LocalDate.of(2021, 7, 15)) + .withEndDate(LocalDate.of(2022, 7, 15)) + .build(); + EXPERIENCE_1.setDeletedAt(UNIX_EPOCH); + } - public static final Experience EXPERIENCE_1 = new Experience.Builder() + public static final Experience EXPERIENCE_2 = new Experience.Builder() .withId(2L) .withMember(MEMBER_1) .withName("Experience 2") @@ -215,7 +240,7 @@ private TestData() { .withEndDate(LocalDate.of(2023, 7, 15)) .build(); - public static final Experience EXPERIENCE_2 = new Experience.Builder() + public static final Experience EXPERIENCE_3 = new Experience.Builder() .withId(3L) .withMember(MEMBER_2) .withName("Experience 3") @@ -227,6 +252,204 @@ private TestData() { .withEndDate(LocalDate.of(2024, 7, 15)) .build(); + public static final List MEMBER_1_OVERVIEWS = List.of( + MemberOverview.Builder.builder() + .withUniqueRowId(1L) + .withMemberId(TestData.MEMBER_1.getId()) + .withFirstName(TestData.MEMBER_1.getFirstName()) + .withLastName(TestData.MEMBER_1.getLastName()) + .withAbbreviation(TestData.MEMBER_1.getAbbreviation()) + .withEmploymentState(TestData.MEMBER_1.getEmploymentState()) + .withDateOfHire(TestData.MEMBER_1.getDateOfHire()) + .withBirthDate(TestData.MEMBER_1.getBirthDate()) + .withOrganisationUnitName(TestData.MEMBER_1.getOrganisationUnit().getName()) + .withCertificateId(TestData.CERTIFICATE_1.getId()) + .withCertificateCompletedAt(TestData.CERTIFICATE_1.getCompletedAt()) + .withCertificateValidUntil(TestData.CERTIFICATE_1.getValidUntil()) + .withCertificateComment(TestData.CERTIFICATE_1.getComment()) + .withCertificateTypeName(TestData.CERTIFICATE_1.getCertificateType().getName()) + .withCertificateTypeComment(TestData.CERTIFICATE_1.getCertificateType().getComment()) + .withleadershipTypeKind(CertificateKind.CERTIFICATE) + .withDegreeId(TestData.DEGREE_1.getId()) + .withDegreeName(TestData.DEGREE_1.getName()) + .withDegreeStartDate(TestData.DEGREE_1.getStartDate()) + .withDegreeEndDate(TestData.DEGREE_1.getEndDate()) + .withDegreeComment(TestData.DEGREE_1.getComment()) + .withDegreeTypeName(TestData.DEGREE_1.getDegreeType().getName()) + .withExperienceId(TestData.EXPERIENCE_1.getId()) + .withExperienceName(TestData.EXPERIENCE_1.getName()) + .withExperienceEmployer(TestData.EXPERIENCE_1.getEmployer()) + .withExperienceStartDate(TestData.EXPERIENCE_1.getStartDate()) + .withExperienceEndDate(TestData.EXPERIENCE_1.getEndDate()) + .withExperienceComment(TestData.EXPERIENCE_1.getComment()) + .withExperienceTypeName(TestData.EXPERIENCE_1.getType().getName()) + .build(), + + MemberOverview.Builder.builder() + .withUniqueRowId(2L) + .withMemberId(TestData.MEMBER_1.getId()) + .withFirstName(TestData.MEMBER_1.getFirstName()) + .withLastName(TestData.MEMBER_1.getLastName()) + .withAbbreviation(TestData.MEMBER_1.getAbbreviation()) + .withEmploymentState(TestData.MEMBER_1.getEmploymentState()) + .withDateOfHire(TestData.MEMBER_1.getDateOfHire()) + .withBirthDate(TestData.MEMBER_1.getBirthDate()) + .withOrganisationUnitName(TestData.MEMBER_1.getOrganisationUnit().getName()) + .withCertificateId(TestData.CERTIFICATE_1.getId()) + .withCertificateCompletedAt(TestData.CERTIFICATE_1.getCompletedAt()) + .withCertificateValidUntil(TestData.CERTIFICATE_1.getValidUntil()) + .withCertificateComment(TestData.CERTIFICATE_1.getComment()) + .withCertificateTypeName(TestData.CERTIFICATE_1.getCertificateType().getName()) + .withCertificateTypeComment(TestData.CERTIFICATE_1.getCertificateType().getComment()) + .withleadershipTypeKind(CertificateKind.CERTIFICATE) + .withDegreeId(TestData.DEGREE_1.getId()) + .withDegreeName(TestData.DEGREE_1.getName()) + .withDegreeStartDate(TestData.DEGREE_1.getStartDate()) + .withDegreeEndDate(TestData.DEGREE_1.getEndDate()) + .withDegreeComment(TestData.DEGREE_1.getComment()) + .withDegreeTypeName(TestData.DEGREE_1.getDegreeType().getName()) + .withExperienceId(TestData.EXPERIENCE_2.getId()) + .withExperienceName(TestData.EXPERIENCE_2.getName()) + .withExperienceEmployer(TestData.EXPERIENCE_2.getEmployer()) + .withExperienceStartDate(TestData.EXPERIENCE_2.getStartDate()) + .withExperienceEndDate(TestData.EXPERIENCE_2.getEndDate()) + .withExperienceComment(TestData.EXPERIENCE_2.getComment()) + .withExperienceTypeName(TestData.EXPERIENCE_2.getType().getName()) + .build(), + + MemberOverview.Builder.builder() + .withUniqueRowId(3L) + .withMemberId(TestData.MEMBER_1.getId()) + .withFirstName(TestData.MEMBER_1.getFirstName()) + .withLastName(TestData.MEMBER_1.getLastName()) + .withAbbreviation(TestData.MEMBER_1.getAbbreviation()) + .withEmploymentState(TestData.MEMBER_1.getEmploymentState()) + .withDateOfHire(TestData.MEMBER_1.getDateOfHire()) + .withBirthDate(TestData.MEMBER_1.getBirthDate()) + .withOrganisationUnitName(TestData.MEMBER_1.getOrganisationUnit().getName()) + .withCertificateId(TestData.CERTIFICATE_4.getId()) + .withCertificateCompletedAt(TestData.CERTIFICATE_4.getCompletedAt()) + .withCertificateValidUntil(TestData.CERTIFICATE_4.getValidUntil()) + .withCertificateComment(TestData.CERTIFICATE_4.getComment()) + .withCertificateTypeName(TestData.CERTIFICATE_4.getCertificateType().getName()) + .withCertificateTypeComment(TestData.CERTIFICATE_4.getCertificateType().getComment()) + .withleadershipTypeKind(CertificateKind.CERTIFICATE) + .withDegreeId(TestData.DEGREE_1.getId()) + .withDegreeName(TestData.DEGREE_1.getName()) + .withDegreeStartDate(TestData.DEGREE_1.getStartDate()) + .withDegreeEndDate(TestData.DEGREE_1.getEndDate()) + .withDegreeComment(TestData.DEGREE_1.getComment()) + .withDegreeTypeName(TestData.DEGREE_1.getDegreeType().getName()) + .withExperienceId(TestData.EXPERIENCE_1.getId()) + .withExperienceName(TestData.EXPERIENCE_1.getName()) + .withExperienceEmployer(TestData.EXPERIENCE_1.getEmployer()) + .withExperienceStartDate(TestData.EXPERIENCE_1.getStartDate()) + .withExperienceEndDate(TestData.EXPERIENCE_1.getEndDate()) + .withExperienceComment(TestData.EXPERIENCE_1.getComment()) + .withExperienceTypeName(TestData.EXPERIENCE_1.getType().getName()) + .build(), + + MemberOverview.Builder.builder() + .withUniqueRowId(4L) + .withMemberId(TestData.MEMBER_1.getId()) + .withFirstName(TestData.MEMBER_1.getFirstName()) + .withLastName(TestData.MEMBER_1.getLastName()) + .withAbbreviation(TestData.MEMBER_1.getAbbreviation()) + .withEmploymentState(TestData.MEMBER_1.getEmploymentState()) + .withDateOfHire(TestData.MEMBER_1.getDateOfHire()) + .withBirthDate(TestData.MEMBER_1.getBirthDate()) + .withOrganisationUnitName(TestData.MEMBER_1.getOrganisationUnit().getName()) + .withCertificateId(TestData.CERTIFICATE_4.getId()) + .withCertificateCompletedAt(TestData.CERTIFICATE_4.getCompletedAt()) + .withCertificateValidUntil(TestData.CERTIFICATE_4.getValidUntil()) + .withCertificateComment(TestData.CERTIFICATE_4.getComment()) + .withCertificateTypeName(TestData.CERTIFICATE_4.getCertificateType().getName()) + .withCertificateTypeComment(TestData.CERTIFICATE_4.getCertificateType().getComment()) + .withleadershipTypeKind(CertificateKind.CERTIFICATE) + .withDegreeId(TestData.DEGREE_1.getId()) + .withDegreeName(TestData.DEGREE_1.getName()) + .withDegreeStartDate(TestData.DEGREE_1.getStartDate()) + .withDegreeEndDate(TestData.DEGREE_1.getEndDate()) + .withDegreeComment(TestData.DEGREE_1.getComment()) + .withDegreeTypeName(TestData.DEGREE_1.getDegreeType().getName()) + .withExperienceId(TestData.EXPERIENCE_2.getId()) + .withExperienceName(TestData.EXPERIENCE_2.getName()) + .withExperienceEmployer(TestData.EXPERIENCE_2.getEmployer()) + .withExperienceStartDate(TestData.EXPERIENCE_2.getStartDate()) + .withExperienceEndDate(TestData.EXPERIENCE_2.getEndDate()) + .withExperienceComment(TestData.EXPERIENCE_2.getComment()) + .withExperienceTypeName(TestData.EXPERIENCE_2.getType().getName()) + .build() + ); + + public static final MemberOverviewDto MEMBER_1_OVERVIEW_DTO = new MemberOverviewDto( + new MemberOverviewMemberDto( + TestData.MEMBER_1.getId(), + TestData.MEMBER_1.getFirstName(), + TestData.MEMBER_1.getLastName(), + TestData.MEMBER_1.getEmploymentState(), + TestData.MEMBER_1.getAbbreviation(), + TestData.MEMBER_1.getDateOfHire(), + TestData.MEMBER_1.getBirthDate(), + TestData.MEMBER_1.getOrganisationUnit().getName() + ), + new MemberCvDto( + List.of( + new MemberOverviewDegreeDto( + TestData.DEGREE_1.getId(), + TestData.DEGREE_1.getName(), + new MemberOverviewDegreeTypeDto(TestData.DEGREE_1.getDegreeType().getName()), + TestData.DEGREE_1.getStartDate(), + TestData.DEGREE_1.getEndDate(), + TestData.DEGREE_1.getComment() + ) + ), + List.of( + new MemberOverviewExperienceDto( + TestData.EXPERIENCE_1.getId(), + TestData.EXPERIENCE_1.getName(), + TestData.EXPERIENCE_1.getEmployer(), + new MemberOverviewExperienceTypeDto(TestData.EXPERIENCE_1.getType().getName()), + TestData.EXPERIENCE_1.getComment(), + TestData.EXPERIENCE_1.getStartDate(), + TestData.EXPERIENCE_1.getEndDate() + ), + new MemberOverviewExperienceDto( + TestData.EXPERIENCE_2.getId(), + TestData.EXPERIENCE_2.getName(), + TestData.EXPERIENCE_2.getEmployer(), + new MemberOverviewExperienceTypeDto(TestData.EXPERIENCE_2.getType().getName()), + TestData.EXPERIENCE_2.getComment(), + TestData.EXPERIENCE_2.getStartDate(), + TestData.EXPERIENCE_2.getEndDate() + ) + ), + List.of( + new MemberOverviewCertificateDto( + TestData.CERTIFICATE_1.getId(), + new MemberOverviewCertificateTypeDto( + TestData.CERTIFICATE_1.getCertificateType().getName(), + TestData.CERTIFICATE_1.getCertificateType().getComment() + ), + TestData.CERTIFICATE_1.getCompletedAt(), + TestData.CERTIFICATE_1.getValidUntil(), + TestData.CERTIFICATE_1.getComment() + ), + new MemberOverviewCertificateDto( + TestData.CERTIFICATE_4.getId(), + new MemberOverviewCertificateTypeDto( + TestData.CERTIFICATE_4.getCertificateType().getName(), + TestData.CERTIFICATE_4.getCertificateType().getComment() + ), + TestData.CERTIFICATE_4.getCompletedAt(), + TestData.CERTIFICATE_4.getValidUntil(), + TestData.CERTIFICATE_4.getComment() + ) + ), + List.of() + ) + ); + public static final Calculation CALCULATION_1 = new Calculation(1L, MEMBER_1, ROLE_2, @@ -271,7 +494,7 @@ private TestData() { public static final List EXPERIENCE_TYPES = List.of(EXP_TYPE_1, EXP_TYPE_2); - public static final List EXPERIENCES = List.of(EXPERIENCE_1, EXPERIENCE_2); + public static final List EXPERIENCES = List.of(EXPERIENCE_2, EXPERIENCE_3); public static final List CALCULATIONS = List.of(CALCULATION_1, CALCULATION_2, CALCULATION_3); From c751a41adb2207c8458d869d3b6574a93b905cd0 Mon Sep 17 00:00:00 2001 From: Nevio Di Gennaro Date: Tue, 23 Dec 2025 09:21:50 +0100 Subject: [PATCH 13/30] refactor: format code #50 --- .../pcts/mapper/MemberOverviewMapperTest.java | 16 +- .../java/ch/puzzle/pcts/util/TestData.java | 360 ++++++++++-------- 2 files changed, 199 insertions(+), 177 deletions(-) diff --git a/backend/src/test/java/ch/puzzle/pcts/mapper/MemberOverviewMapperTest.java b/backend/src/test/java/ch/puzzle/pcts/mapper/MemberOverviewMapperTest.java index d4af202a5..77ec1a703 100644 --- a/backend/src/test/java/ch/puzzle/pcts/mapper/MemberOverviewMapperTest.java +++ b/backend/src/test/java/ch/puzzle/pcts/mapper/MemberOverviewMapperTest.java @@ -1,5 +1,7 @@ package ch.puzzle.pcts.mapper; +import static org.assertj.core.api.Assertions.assertThat; + import ch.puzzle.pcts.dto.memberoverview.MemberCvDto; import ch.puzzle.pcts.dto.memberoverview.MemberOverviewDto; import ch.puzzle.pcts.dto.memberoverview.MemberOverviewMemberDto; @@ -8,17 +10,13 @@ import ch.puzzle.pcts.dto.memberoverview.experience.MemberOverviewExperienceDto; import ch.puzzle.pcts.model.member.EmploymentState; import ch.puzzle.pcts.util.TestData; +import java.time.LocalDate; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.InjectMocks; import org.mockito.junit.jupiter.MockitoExtension; -import java.time.LocalDate; - -import static org.assertj.core.api.Assertions.assertThat; - - @ExtendWith(MockitoExtension.class) class MemberOverviewMapperTest { @@ -56,14 +54,10 @@ void shouldReturnMemberOverviewDto() { assertThat(degree.comment()).isEqualTo("Comment"); assertThat(cv.experiences()).hasSize(2); - assertThat(cv.experiences()) - .extracting(MemberOverviewExperienceDto::id) - .containsExactlyInAnyOrder(1L, 2L); + assertThat(cv.experiences()).extracting(MemberOverviewExperienceDto::id).containsExactlyInAnyOrder(1L, 2L); assertThat(cv.certificates()).hasSize(2); - assertThat(cv.certificates()) - .extracting(MemberOverviewCertificateDto::id) - .containsExactlyInAnyOrder(1L, 4L); + assertThat(cv.certificates()).extracting(MemberOverviewCertificateDto::id).containsExactlyInAnyOrder(1L, 4L); assertThat(cv.leadershipExperiences()).isEmpty(); } diff --git a/backend/src/test/java/ch/puzzle/pcts/util/TestData.java b/backend/src/test/java/ch/puzzle/pcts/util/TestData.java index 77cc3bb45..0ef16626d 100644 --- a/backend/src/test/java/ch/puzzle/pcts/util/TestData.java +++ b/backend/src/test/java/ch/puzzle/pcts/util/TestData.java @@ -252,8 +252,9 @@ private TestData() { .withEndDate(LocalDate.of(2024, 7, 15)) .build(); - public static final List MEMBER_1_OVERVIEWS = List.of( - MemberOverview.Builder.builder() + public static final List MEMBER_1_OVERVIEWS = List + .of(MemberOverview.Builder + .builder() .withUniqueRowId(1L) .withMemberId(TestData.MEMBER_1.getId()) .withFirstName(TestData.MEMBER_1.getFirstName()) @@ -285,170 +286,197 @@ private TestData() { .withExperienceTypeName(TestData.EXPERIENCE_1.getType().getName()) .build(), - MemberOverview.Builder.builder() - .withUniqueRowId(2L) - .withMemberId(TestData.MEMBER_1.getId()) - .withFirstName(TestData.MEMBER_1.getFirstName()) - .withLastName(TestData.MEMBER_1.getLastName()) - .withAbbreviation(TestData.MEMBER_1.getAbbreviation()) - .withEmploymentState(TestData.MEMBER_1.getEmploymentState()) - .withDateOfHire(TestData.MEMBER_1.getDateOfHire()) - .withBirthDate(TestData.MEMBER_1.getBirthDate()) - .withOrganisationUnitName(TestData.MEMBER_1.getOrganisationUnit().getName()) - .withCertificateId(TestData.CERTIFICATE_1.getId()) - .withCertificateCompletedAt(TestData.CERTIFICATE_1.getCompletedAt()) - .withCertificateValidUntil(TestData.CERTIFICATE_1.getValidUntil()) - .withCertificateComment(TestData.CERTIFICATE_1.getComment()) - .withCertificateTypeName(TestData.CERTIFICATE_1.getCertificateType().getName()) - .withCertificateTypeComment(TestData.CERTIFICATE_1.getCertificateType().getComment()) - .withleadershipTypeKind(CertificateKind.CERTIFICATE) - .withDegreeId(TestData.DEGREE_1.getId()) - .withDegreeName(TestData.DEGREE_1.getName()) - .withDegreeStartDate(TestData.DEGREE_1.getStartDate()) - .withDegreeEndDate(TestData.DEGREE_1.getEndDate()) - .withDegreeComment(TestData.DEGREE_1.getComment()) - .withDegreeTypeName(TestData.DEGREE_1.getDegreeType().getName()) - .withExperienceId(TestData.EXPERIENCE_2.getId()) - .withExperienceName(TestData.EXPERIENCE_2.getName()) - .withExperienceEmployer(TestData.EXPERIENCE_2.getEmployer()) - .withExperienceStartDate(TestData.EXPERIENCE_2.getStartDate()) - .withExperienceEndDate(TestData.EXPERIENCE_2.getEndDate()) - .withExperienceComment(TestData.EXPERIENCE_2.getComment()) - .withExperienceTypeName(TestData.EXPERIENCE_2.getType().getName()) - .build(), - - MemberOverview.Builder.builder() - .withUniqueRowId(3L) - .withMemberId(TestData.MEMBER_1.getId()) - .withFirstName(TestData.MEMBER_1.getFirstName()) - .withLastName(TestData.MEMBER_1.getLastName()) - .withAbbreviation(TestData.MEMBER_1.getAbbreviation()) - .withEmploymentState(TestData.MEMBER_1.getEmploymentState()) - .withDateOfHire(TestData.MEMBER_1.getDateOfHire()) - .withBirthDate(TestData.MEMBER_1.getBirthDate()) - .withOrganisationUnitName(TestData.MEMBER_1.getOrganisationUnit().getName()) - .withCertificateId(TestData.CERTIFICATE_4.getId()) - .withCertificateCompletedAt(TestData.CERTIFICATE_4.getCompletedAt()) - .withCertificateValidUntil(TestData.CERTIFICATE_4.getValidUntil()) - .withCertificateComment(TestData.CERTIFICATE_4.getComment()) - .withCertificateTypeName(TestData.CERTIFICATE_4.getCertificateType().getName()) - .withCertificateTypeComment(TestData.CERTIFICATE_4.getCertificateType().getComment()) - .withleadershipTypeKind(CertificateKind.CERTIFICATE) - .withDegreeId(TestData.DEGREE_1.getId()) - .withDegreeName(TestData.DEGREE_1.getName()) - .withDegreeStartDate(TestData.DEGREE_1.getStartDate()) - .withDegreeEndDate(TestData.DEGREE_1.getEndDate()) - .withDegreeComment(TestData.DEGREE_1.getComment()) - .withDegreeTypeName(TestData.DEGREE_1.getDegreeType().getName()) - .withExperienceId(TestData.EXPERIENCE_1.getId()) - .withExperienceName(TestData.EXPERIENCE_1.getName()) - .withExperienceEmployer(TestData.EXPERIENCE_1.getEmployer()) - .withExperienceStartDate(TestData.EXPERIENCE_1.getStartDate()) - .withExperienceEndDate(TestData.EXPERIENCE_1.getEndDate()) - .withExperienceComment(TestData.EXPERIENCE_1.getComment()) - .withExperienceTypeName(TestData.EXPERIENCE_1.getType().getName()) - .build(), - - MemberOverview.Builder.builder() - .withUniqueRowId(4L) - .withMemberId(TestData.MEMBER_1.getId()) - .withFirstName(TestData.MEMBER_1.getFirstName()) - .withLastName(TestData.MEMBER_1.getLastName()) - .withAbbreviation(TestData.MEMBER_1.getAbbreviation()) - .withEmploymentState(TestData.MEMBER_1.getEmploymentState()) - .withDateOfHire(TestData.MEMBER_1.getDateOfHire()) - .withBirthDate(TestData.MEMBER_1.getBirthDate()) - .withOrganisationUnitName(TestData.MEMBER_1.getOrganisationUnit().getName()) - .withCertificateId(TestData.CERTIFICATE_4.getId()) - .withCertificateCompletedAt(TestData.CERTIFICATE_4.getCompletedAt()) - .withCertificateValidUntil(TestData.CERTIFICATE_4.getValidUntil()) - .withCertificateComment(TestData.CERTIFICATE_4.getComment()) - .withCertificateTypeName(TestData.CERTIFICATE_4.getCertificateType().getName()) - .withCertificateTypeComment(TestData.CERTIFICATE_4.getCertificateType().getComment()) - .withleadershipTypeKind(CertificateKind.CERTIFICATE) - .withDegreeId(TestData.DEGREE_1.getId()) - .withDegreeName(TestData.DEGREE_1.getName()) - .withDegreeStartDate(TestData.DEGREE_1.getStartDate()) - .withDegreeEndDate(TestData.DEGREE_1.getEndDate()) - .withDegreeComment(TestData.DEGREE_1.getComment()) - .withDegreeTypeName(TestData.DEGREE_1.getDegreeType().getName()) - .withExperienceId(TestData.EXPERIENCE_2.getId()) - .withExperienceName(TestData.EXPERIENCE_2.getName()) - .withExperienceEmployer(TestData.EXPERIENCE_2.getEmployer()) - .withExperienceStartDate(TestData.EXPERIENCE_2.getStartDate()) - .withExperienceEndDate(TestData.EXPERIENCE_2.getEndDate()) - .withExperienceComment(TestData.EXPERIENCE_2.getComment()) - .withExperienceTypeName(TestData.EXPERIENCE_2.getType().getName()) - .build() - ); - - public static final MemberOverviewDto MEMBER_1_OVERVIEW_DTO = new MemberOverviewDto( - new MemberOverviewMemberDto( - TestData.MEMBER_1.getId(), - TestData.MEMBER_1.getFirstName(), - TestData.MEMBER_1.getLastName(), - TestData.MEMBER_1.getEmploymentState(), - TestData.MEMBER_1.getAbbreviation(), - TestData.MEMBER_1.getDateOfHire(), - TestData.MEMBER_1.getBirthDate(), - TestData.MEMBER_1.getOrganisationUnit().getName() - ), - new MemberCvDto( - List.of( - new MemberOverviewDegreeDto( - TestData.DEGREE_1.getId(), - TestData.DEGREE_1.getName(), - new MemberOverviewDegreeTypeDto(TestData.DEGREE_1.getDegreeType().getName()), - TestData.DEGREE_1.getStartDate(), - TestData.DEGREE_1.getEndDate(), - TestData.DEGREE_1.getComment() - ) - ), - List.of( - new MemberOverviewExperienceDto( - TestData.EXPERIENCE_1.getId(), - TestData.EXPERIENCE_1.getName(), - TestData.EXPERIENCE_1.getEmployer(), - new MemberOverviewExperienceTypeDto(TestData.EXPERIENCE_1.getType().getName()), - TestData.EXPERIENCE_1.getComment(), - TestData.EXPERIENCE_1.getStartDate(), - TestData.EXPERIENCE_1.getEndDate() - ), - new MemberOverviewExperienceDto( - TestData.EXPERIENCE_2.getId(), - TestData.EXPERIENCE_2.getName(), - TestData.EXPERIENCE_2.getEmployer(), - new MemberOverviewExperienceTypeDto(TestData.EXPERIENCE_2.getType().getName()), - TestData.EXPERIENCE_2.getComment(), - TestData.EXPERIENCE_2.getStartDate(), - TestData.EXPERIENCE_2.getEndDate() - ) - ), - List.of( - new MemberOverviewCertificateDto( - TestData.CERTIFICATE_1.getId(), - new MemberOverviewCertificateTypeDto( - TestData.CERTIFICATE_1.getCertificateType().getName(), - TestData.CERTIFICATE_1.getCertificateType().getComment() - ), - TestData.CERTIFICATE_1.getCompletedAt(), - TestData.CERTIFICATE_1.getValidUntil(), - TestData.CERTIFICATE_1.getComment() - ), - new MemberOverviewCertificateDto( - TestData.CERTIFICATE_4.getId(), - new MemberOverviewCertificateTypeDto( - TestData.CERTIFICATE_4.getCertificateType().getName(), - TestData.CERTIFICATE_4.getCertificateType().getComment() - ), - TestData.CERTIFICATE_4.getCompletedAt(), - TestData.CERTIFICATE_4.getValidUntil(), - TestData.CERTIFICATE_4.getComment() - ) - ), - List.of() - ) - ); + MemberOverview.Builder + .builder() + .withUniqueRowId(2L) + .withMemberId(TestData.MEMBER_1.getId()) + .withFirstName(TestData.MEMBER_1.getFirstName()) + .withLastName(TestData.MEMBER_1.getLastName()) + .withAbbreviation(TestData.MEMBER_1.getAbbreviation()) + .withEmploymentState(TestData.MEMBER_1.getEmploymentState()) + .withDateOfHire(TestData.MEMBER_1.getDateOfHire()) + .withBirthDate(TestData.MEMBER_1.getBirthDate()) + .withOrganisationUnitName(TestData.MEMBER_1.getOrganisationUnit().getName()) + .withCertificateId(TestData.CERTIFICATE_1.getId()) + .withCertificateCompletedAt(TestData.CERTIFICATE_1.getCompletedAt()) + .withCertificateValidUntil(TestData.CERTIFICATE_1.getValidUntil()) + .withCertificateComment(TestData.CERTIFICATE_1.getComment()) + .withCertificateTypeName(TestData.CERTIFICATE_1.getCertificateType().getName()) + .withCertificateTypeComment(TestData.CERTIFICATE_1.getCertificateType().getComment()) + .withleadershipTypeKind(CertificateKind.CERTIFICATE) + .withDegreeId(TestData.DEGREE_1.getId()) + .withDegreeName(TestData.DEGREE_1.getName()) + .withDegreeStartDate(TestData.DEGREE_1.getStartDate()) + .withDegreeEndDate(TestData.DEGREE_1.getEndDate()) + .withDegreeComment(TestData.DEGREE_1.getComment()) + .withDegreeTypeName(TestData.DEGREE_1.getDegreeType().getName()) + .withExperienceId(TestData.EXPERIENCE_2.getId()) + .withExperienceName(TestData.EXPERIENCE_2.getName()) + .withExperienceEmployer(TestData.EXPERIENCE_2.getEmployer()) + .withExperienceStartDate(TestData.EXPERIENCE_2.getStartDate()) + .withExperienceEndDate(TestData.EXPERIENCE_2.getEndDate()) + .withExperienceComment(TestData.EXPERIENCE_2.getComment()) + .withExperienceTypeName(TestData.EXPERIENCE_2.getType().getName()) + .build(), + + MemberOverview.Builder + .builder() + .withUniqueRowId(3L) + .withMemberId(TestData.MEMBER_1.getId()) + .withFirstName(TestData.MEMBER_1.getFirstName()) + .withLastName(TestData.MEMBER_1.getLastName()) + .withAbbreviation(TestData.MEMBER_1.getAbbreviation()) + .withEmploymentState(TestData.MEMBER_1.getEmploymentState()) + .withDateOfHire(TestData.MEMBER_1.getDateOfHire()) + .withBirthDate(TestData.MEMBER_1.getBirthDate()) + .withOrganisationUnitName(TestData.MEMBER_1.getOrganisationUnit().getName()) + .withCertificateId(TestData.CERTIFICATE_4.getId()) + .withCertificateCompletedAt(TestData.CERTIFICATE_4.getCompletedAt()) + .withCertificateValidUntil(TestData.CERTIFICATE_4.getValidUntil()) + .withCertificateComment(TestData.CERTIFICATE_4.getComment()) + .withCertificateTypeName(TestData.CERTIFICATE_4.getCertificateType().getName()) + .withCertificateTypeComment(TestData.CERTIFICATE_4.getCertificateType().getComment()) + .withleadershipTypeKind(CertificateKind.CERTIFICATE) + .withDegreeId(TestData.DEGREE_1.getId()) + .withDegreeName(TestData.DEGREE_1.getName()) + .withDegreeStartDate(TestData.DEGREE_1.getStartDate()) + .withDegreeEndDate(TestData.DEGREE_1.getEndDate()) + .withDegreeComment(TestData.DEGREE_1.getComment()) + .withDegreeTypeName(TestData.DEGREE_1.getDegreeType().getName()) + .withExperienceId(TestData.EXPERIENCE_1.getId()) + .withExperienceName(TestData.EXPERIENCE_1.getName()) + .withExperienceEmployer(TestData.EXPERIENCE_1.getEmployer()) + .withExperienceStartDate(TestData.EXPERIENCE_1.getStartDate()) + .withExperienceEndDate(TestData.EXPERIENCE_1.getEndDate()) + .withExperienceComment(TestData.EXPERIENCE_1.getComment()) + .withExperienceTypeName(TestData.EXPERIENCE_1.getType().getName()) + .build(), + + MemberOverview.Builder + .builder() + .withUniqueRowId(4L) + .withMemberId(TestData.MEMBER_1.getId()) + .withFirstName(TestData.MEMBER_1.getFirstName()) + .withLastName(TestData.MEMBER_1.getLastName()) + .withAbbreviation(TestData.MEMBER_1.getAbbreviation()) + .withEmploymentState(TestData.MEMBER_1.getEmploymentState()) + .withDateOfHire(TestData.MEMBER_1.getDateOfHire()) + .withBirthDate(TestData.MEMBER_1.getBirthDate()) + .withOrganisationUnitName(TestData.MEMBER_1.getOrganisationUnit().getName()) + .withCertificateId(TestData.CERTIFICATE_4.getId()) + .withCertificateCompletedAt(TestData.CERTIFICATE_4.getCompletedAt()) + .withCertificateValidUntil(TestData.CERTIFICATE_4.getValidUntil()) + .withCertificateComment(TestData.CERTIFICATE_4.getComment()) + .withCertificateTypeName(TestData.CERTIFICATE_4.getCertificateType().getName()) + .withCertificateTypeComment(TestData.CERTIFICATE_4.getCertificateType().getComment()) + .withleadershipTypeKind(CertificateKind.CERTIFICATE) + .withDegreeId(TestData.DEGREE_1.getId()) + .withDegreeName(TestData.DEGREE_1.getName()) + .withDegreeStartDate(TestData.DEGREE_1.getStartDate()) + .withDegreeEndDate(TestData.DEGREE_1.getEndDate()) + .withDegreeComment(TestData.DEGREE_1.getComment()) + .withDegreeTypeName(TestData.DEGREE_1.getDegreeType().getName()) + .withExperienceId(TestData.EXPERIENCE_2.getId()) + .withExperienceName(TestData.EXPERIENCE_2.getName()) + .withExperienceEmployer(TestData.EXPERIENCE_2.getEmployer()) + .withExperienceStartDate(TestData.EXPERIENCE_2.getStartDate()) + .withExperienceEndDate(TestData.EXPERIENCE_2.getEndDate()) + .withExperienceComment(TestData.EXPERIENCE_2.getComment()) + .withExperienceTypeName(TestData.EXPERIENCE_2.getType().getName()) + .build()); + + public static final MemberOverviewDto MEMBER_1_OVERVIEW_DTO = new MemberOverviewDto(new MemberOverviewMemberDto(TestData.MEMBER_1 + .getId(), + TestData.MEMBER_1 + .getFirstName(), + TestData.MEMBER_1 + .getLastName(), + TestData.MEMBER_1 + .getEmploymentState(), + TestData.MEMBER_1 + .getAbbreviation(), + TestData.MEMBER_1 + .getDateOfHire(), + TestData.MEMBER_1 + .getBirthDate(), + TestData.MEMBER_1 + .getOrganisationUnit() + .getName()), + new MemberCvDto(List + .of(new MemberOverviewDegreeDto(TestData.DEGREE_1 + .getId(), + TestData.DEGREE_1 + .getName(), + new MemberOverviewDegreeTypeDto(TestData.DEGREE_1 + .getDegreeType() + .getName()), + TestData.DEGREE_1 + .getStartDate(), + TestData.DEGREE_1 + .getEndDate(), + TestData.DEGREE_1 + .getComment())), + List + .of(new MemberOverviewExperienceDto(TestData.EXPERIENCE_1 + .getId(), + TestData.EXPERIENCE_1 + .getName(), + TestData.EXPERIENCE_1 + .getEmployer(), + new MemberOverviewExperienceTypeDto(TestData.EXPERIENCE_1 + .getType() + .getName()), + TestData.EXPERIENCE_1 + .getComment(), + TestData.EXPERIENCE_1 + .getStartDate(), + TestData.EXPERIENCE_1 + .getEndDate()), + new MemberOverviewExperienceDto(TestData.EXPERIENCE_2 + .getId(), + TestData.EXPERIENCE_2 + .getName(), + TestData.EXPERIENCE_2 + .getEmployer(), + new MemberOverviewExperienceTypeDto(TestData.EXPERIENCE_2 + .getType() + .getName()), + TestData.EXPERIENCE_2 + .getComment(), + TestData.EXPERIENCE_2 + .getStartDate(), + TestData.EXPERIENCE_2 + .getEndDate())), + List + .of(new MemberOverviewCertificateDto(TestData.CERTIFICATE_1 + .getId(), + new MemberOverviewCertificateTypeDto(TestData.CERTIFICATE_1 + .getCertificateType() + .getName(), + TestData.CERTIFICATE_1 + .getCertificateType() + .getComment()), + TestData.CERTIFICATE_1 + .getCompletedAt(), + TestData.CERTIFICATE_1 + .getValidUntil(), + TestData.CERTIFICATE_1 + .getComment()), + new MemberOverviewCertificateDto(TestData.CERTIFICATE_4 + .getId(), + new MemberOverviewCertificateTypeDto(TestData.CERTIFICATE_4 + .getCertificateType() + .getName(), + TestData.CERTIFICATE_4 + .getCertificateType() + .getComment()), + TestData.CERTIFICATE_4 + .getCompletedAt(), + TestData.CERTIFICATE_4 + .getValidUntil(), + TestData.CERTIFICATE_4 + .getComment())), + List.of())); public static final Calculation CALCULATION_1 = new Calculation(1L, MEMBER_1, From 4c0cb7536ab101db566b1b4f44f0456cdaae64c7 Mon Sep 17 00:00:00 2001 From: Nevio Di Gennaro Date: Tue, 23 Dec 2025 10:54:17 +0100 Subject: [PATCH 14/30] refactor: import static constants instead of accessing via class #50 --- .../controller/MemberOverviewController.java | 10 +- .../pcts/mapper/MemberOverviewMapper.java | 42 +-- .../MemberOverviewControllerIT.java | 7 +- .../pcts/mapper/MemberOverviewMapperTest.java | 4 +- .../CalculationPersistenceServiceIT.java | 4 +- .../CertificatePersistenceServiceIT.java | 8 +- .../CertificateTypePersistenceServiceIT.java | 4 +- .../DegreePersistenceServiceIT.java | 9 +- .../DegreeTypePersistenceServiceIT.java | 5 +- .../ExperiencePersistenceServiceIT.java | 9 +- .../ExperienceTypePersistenceServiceIT.java | 5 +- .../LeadershipTypePersistenceServiceIT.java | 4 +- .../MemberOverviewPersistenceServiceIT.java | 4 +- .../MemberPersistenceServiceIT.java | 8 +- .../OrganisationUnitPersistenceServiceIT.java | 4 +- .../persistence/RolePersistenceServiceIT.java | 4 +- .../persistence/TagPersistenceServiceIT.java | 4 +- .../java/ch/puzzle/pcts/util/TestData.java | 296 +++++++++--------- 18 files changed, 220 insertions(+), 211 deletions(-) diff --git a/backend/src/main/java/ch/puzzle/pcts/controller/MemberOverviewController.java b/backend/src/main/java/ch/puzzle/pcts/controller/MemberOverviewController.java index a54eee092..b26bf7b0c 100644 --- a/backend/src/main/java/ch/puzzle/pcts/controller/MemberOverviewController.java +++ b/backend/src/main/java/ch/puzzle/pcts/controller/MemberOverviewController.java @@ -30,13 +30,13 @@ public MemberOverviewController(MemberOverviewMapper mapper, MemberOverviewBusin this.service = service; } - @Operation(summary = "Get the member-overview by ID") + @Operation(summary = "Get the member-overview by memberId") @ApiResponse(responseCode = "200", description = "Successfully retrieved the member-overview.", content = { @Content(mediaType = "application/json", schema = @Schema(implementation = MemberDto.class)) }) - @GetMapping("{memberOverviewId}") - public ResponseEntity getMemberOverviewById(@Parameter(description = "ID of the member-overview to retrieve.", required = true) - @PathVariable Long memberOverviewId) { - List memberOverviews = service.getById(memberOverviewId); + @GetMapping("{memberId}") + public ResponseEntity getMemberOverviewByMemberId(@Parameter(description = "The ID of the member whose overview should be retrieved.", required = true) + @PathVariable Long memberId) { + List memberOverviews = service.getById(memberId); return ResponseEntity.ok(mapper.toDto(memberOverviews)); } } diff --git a/backend/src/main/java/ch/puzzle/pcts/mapper/MemberOverviewMapper.java b/backend/src/main/java/ch/puzzle/pcts/mapper/MemberOverviewMapper.java index 96cfa7eb8..d680719d2 100644 --- a/backend/src/main/java/ch/puzzle/pcts/mapper/MemberOverviewMapper.java +++ b/backend/src/main/java/ch/puzzle/pcts/mapper/MemberOverviewMapper.java @@ -18,24 +18,15 @@ @Component public class MemberOverviewMapper { - public MemberOverviewDto toDto(List entities) { + public MemberOverviewDto toDto(List memberOverviews) { - if (entities == null || entities.isEmpty()) { + if (memberOverviews == null || memberOverviews.isEmpty()) { return null; } - MemberOverview first = entities.getFirst(); + MemberOverviewMemberDto memberDto = getMemberDto(memberOverviews); - MemberOverviewMemberDto memberDto = new MemberOverviewMemberDto(first.getMemberId(), - first.getFirstName(), - first.getLastName(), - first.getEmploymentState(), - first.getAbbreviation(), - first.getDateOfHire(), - first.getBirthDate(), - first.getOrganisationUnitName()); - - List degrees = entities + List degrees = memberOverviews .stream() .filter(e -> e.getDegreeId() != null) .collect(Collectors.groupingBy(MemberOverview::getDegreeId)) @@ -52,7 +43,7 @@ public MemberOverviewDto toDto(List entities) { }) .toList(); - List experiences = entities + List experiences = memberOverviews .stream() .filter(e -> e.getExperienceId() != null) .collect(Collectors.groupingBy(MemberOverview::getExperienceId)) @@ -71,10 +62,9 @@ public MemberOverviewDto toDto(List entities) { }) .toList(); - List certificates = entities + List certificates = memberOverviews .stream() - .filter(e -> e.getCertificateId() != null - && CertificateKind.CERTIFICATE.equals(e.getLeadershipTypeKind())) + .filter(e -> e.getCertificateId() != null && CertificateKind.CERTIFICATE == e.getLeadershipTypeKind()) .collect(Collectors.groupingBy(MemberOverview::getCertificateId)) .values() .stream() @@ -91,10 +81,9 @@ public MemberOverviewDto toDto(List entities) { }) .toList(); - List leadershipExperiences = entities + List leadershipExperiences = memberOverviews .stream() - .filter(e -> e.getCertificateId() != null - && !CertificateKind.CERTIFICATE.equals(e.getLeadershipTypeKind())) + .filter(e -> e.getCertificateId() != null && CertificateKind.CERTIFICATE != e.getLeadershipTypeKind()) .collect(Collectors.groupingBy(MemberOverview::getCertificateId)) .values() .stream() @@ -115,4 +104,17 @@ public MemberOverviewDto toDto(List entities) { return new MemberOverviewDto(memberDto, cvDto); } + + public MemberOverviewMemberDto getMemberDto(List memberOverviews) { + MemberOverview first = memberOverviews.getFirst(); + + return new MemberOverviewMemberDto(first.getMemberId(), + first.getFirstName(), + first.getLastName(), + first.getEmploymentState(), + first.getAbbreviation(), + first.getDateOfHire(), + first.getBirthDate(), + first.getOrganisationUnitName()); + } } diff --git a/backend/src/test/java/ch/puzzle/pcts/controller/MemberOverviewControllerIT.java b/backend/src/test/java/ch/puzzle/pcts/controller/MemberOverviewControllerIT.java index 078a68f8a..5ebf324e7 100644 --- a/backend/src/test/java/ch/puzzle/pcts/controller/MemberOverviewControllerIT.java +++ b/backend/src/test/java/ch/puzzle/pcts/controller/MemberOverviewControllerIT.java @@ -1,5 +1,7 @@ package ch.puzzle.pcts.controller; +import static ch.puzzle.pcts.util.TestData.MEMBER_1_OVERVIEWS; +import static ch.puzzle.pcts.util.TestData.MEMBER_1_OVERVIEW_DTO; import static org.mockito.ArgumentMatchers.anyLong; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; @@ -12,7 +14,6 @@ import ch.puzzle.pcts.model.memberoverview.MemberOverview; import ch.puzzle.pcts.service.business.MemberOverviewBusinessService; import ch.puzzle.pcts.util.JsonDtoMatcher; -import ch.puzzle.pcts.util.TestData; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.SerializationFeature; import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; @@ -55,8 +56,8 @@ class MemberOverviewControllerIT { void shouldGetMemberOverviewById() throws Exception { Long memberId = 1L; - List memberOverviews = TestData.MEMBER_1_OVERVIEWS; - MemberOverviewDto expectedDto = TestData.MEMBER_1_OVERVIEW_DTO; + List memberOverviews = MEMBER_1_OVERVIEWS; + MemberOverviewDto expectedDto = MEMBER_1_OVERVIEW_DTO; BDDMockito.given(service.getById(anyLong())).willReturn(memberOverviews); BDDMockito.given(mapper.toDto(memberOverviews)).willReturn(expectedDto); diff --git a/backend/src/test/java/ch/puzzle/pcts/mapper/MemberOverviewMapperTest.java b/backend/src/test/java/ch/puzzle/pcts/mapper/MemberOverviewMapperTest.java index 77ec1a703..80617eb11 100644 --- a/backend/src/test/java/ch/puzzle/pcts/mapper/MemberOverviewMapperTest.java +++ b/backend/src/test/java/ch/puzzle/pcts/mapper/MemberOverviewMapperTest.java @@ -1,5 +1,6 @@ package ch.puzzle.pcts.mapper; +import static ch.puzzle.pcts.util.TestData.MEMBER_1_OVERVIEWS; import static org.assertj.core.api.Assertions.assertThat; import ch.puzzle.pcts.dto.memberoverview.MemberCvDto; @@ -9,7 +10,6 @@ import ch.puzzle.pcts.dto.memberoverview.degree.MemberOverviewDegreeDto; import ch.puzzle.pcts.dto.memberoverview.experience.MemberOverviewExperienceDto; import ch.puzzle.pcts.model.member.EmploymentState; -import ch.puzzle.pcts.util.TestData; import java.time.LocalDate; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; @@ -26,7 +26,7 @@ class MemberOverviewMapperTest { @DisplayName("Should map List to MemberOverviewDto") @Test void shouldReturnMemberOverviewDto() { - MemberOverviewDto dto = mapper.toDto(TestData.MEMBER_1_OVERVIEWS); + MemberOverviewDto dto = mapper.toDto(MEMBER_1_OVERVIEWS); assertThat(dto).isNotNull(); assertThat(dto.member()).isNotNull(); diff --git a/backend/src/test/java/ch/puzzle/pcts/service/persistence/CalculationPersistenceServiceIT.java b/backend/src/test/java/ch/puzzle/pcts/service/persistence/CalculationPersistenceServiceIT.java index 52b27abfc..871a86d53 100644 --- a/backend/src/test/java/ch/puzzle/pcts/service/persistence/CalculationPersistenceServiceIT.java +++ b/backend/src/test/java/ch/puzzle/pcts/service/persistence/CalculationPersistenceServiceIT.java @@ -1,5 +1,6 @@ package ch.puzzle.pcts.service.persistence; +import static ch.puzzle.pcts.util.TestData.CALCULATIONS; import static org.assertj.core.api.Assertions.assertThat; import static org.junit.jupiter.api.Assertions.assertEquals; @@ -8,7 +9,6 @@ import ch.puzzle.pcts.model.member.Member; import ch.puzzle.pcts.model.role.Role; import ch.puzzle.pcts.repository.CalculationRepository; -import ch.puzzle.pcts.util.TestData; import jakarta.transaction.Transactional; import java.time.LocalDate; import java.util.List; @@ -44,7 +44,7 @@ Calculation getModel() { @Override List getAll() { - return TestData.CALCULATIONS; + return CALCULATIONS; } @DisplayName("Should only have one active Calculation after save when member already has active Calculation for the same role.") diff --git a/backend/src/test/java/ch/puzzle/pcts/service/persistence/CertificatePersistenceServiceIT.java b/backend/src/test/java/ch/puzzle/pcts/service/persistence/CertificatePersistenceServiceIT.java index c7e4da0a2..b0beef188 100644 --- a/backend/src/test/java/ch/puzzle/pcts/service/persistence/CertificatePersistenceServiceIT.java +++ b/backend/src/test/java/ch/puzzle/pcts/service/persistence/CertificatePersistenceServiceIT.java @@ -1,6 +1,7 @@ package ch.puzzle.pcts.service.persistence; import static org.junit.jupiter.api.Assertions.*; +import static ch.puzzle.pcts.util.TestData.*; import ch.puzzle.pcts.model.certificate.Certificate; import ch.puzzle.pcts.model.certificatetype.CertificateKind; @@ -12,7 +13,6 @@ import ch.puzzle.pcts.repository.CertificateTypeRepository; import ch.puzzle.pcts.repository.MemberRepository; import ch.puzzle.pcts.repository.OrganisationUnitRepository; -import ch.puzzle.pcts.util.TestData; import jakarta.transaction.Transactional; import java.math.BigDecimal; import java.time.LocalDate; @@ -44,16 +44,16 @@ Certificate getModel() { return Certificate.Builder .builder() .withId(null) - .withMember(TestData.MEMBER_1) + .withMember(MEMBER_1) .withCompletedAt(LocalDate.of(2021, 7, 15)) .withValidUntil(LocalDate.of(2021, 7, 15)) - .withCertificateType(TestData.CERT_TYPE_1) + .withCertificateType(CERT_TYPE_1) .build(); } @Override List getAll() { - return TestData.CERTIFICATES; + return CERTIFICATES; } @Test diff --git a/backend/src/test/java/ch/puzzle/pcts/service/persistence/CertificateTypePersistenceServiceIT.java b/backend/src/test/java/ch/puzzle/pcts/service/persistence/CertificateTypePersistenceServiceIT.java index 3d1f55880..f2b714f0c 100644 --- a/backend/src/test/java/ch/puzzle/pcts/service/persistence/CertificateTypePersistenceServiceIT.java +++ b/backend/src/test/java/ch/puzzle/pcts/service/persistence/CertificateTypePersistenceServiceIT.java @@ -1,6 +1,7 @@ package ch.puzzle.pcts.service.persistence; import static ch.puzzle.pcts.Constants.CERTIFICATE_TYPE; +import static ch.puzzle.pcts.util.TestData.CERTIFICATE_TYPES; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatThrownBy; @@ -11,7 +12,6 @@ import ch.puzzle.pcts.model.certificatetype.CertificateType; import ch.puzzle.pcts.model.certificatetype.Tag; import ch.puzzle.pcts.repository.CertificateTypeRepository; -import ch.puzzle.pcts.util.TestData; import java.math.BigDecimal; import java.util.List; import java.util.Map; @@ -42,7 +42,7 @@ CertificateType getModel() { @Override List getAll() { - return TestData.CERTIFICATE_TYPES; + return CERTIFICATE_TYPES; } @Override diff --git a/backend/src/test/java/ch/puzzle/pcts/service/persistence/DegreePersistenceServiceIT.java b/backend/src/test/java/ch/puzzle/pcts/service/persistence/DegreePersistenceServiceIT.java index f87c082cb..9a752166b 100644 --- a/backend/src/test/java/ch/puzzle/pcts/service/persistence/DegreePersistenceServiceIT.java +++ b/backend/src/test/java/ch/puzzle/pcts/service/persistence/DegreePersistenceServiceIT.java @@ -1,8 +1,9 @@ package ch.puzzle.pcts.service.persistence; +import static ch.puzzle.pcts.util.TestData.*; + import ch.puzzle.pcts.model.degree.Degree; import ch.puzzle.pcts.repository.DegreeRepository; -import ch.puzzle.pcts.util.TestData; import java.time.LocalDate; import java.util.List; import org.springframework.beans.factory.annotation.Autowired; @@ -19,11 +20,11 @@ Degree getModel() { return Degree.Builder .builder() .withId(null) - .withMember(TestData.MEMBER_1) + .withMember(MEMBER_1) .withName("Degree 1") .withInstitution("Institution") .withCompleted(true) - .withDegreeType(TestData.DEGREE_TYPE_1) + .withDegreeType(DEGREE_TYPE_1) .withStartDate(LocalDate.of(2015, 9, 1)) .withEndDate(LocalDate.of(2020, 6, 1)) .withComment("Comment") @@ -32,6 +33,6 @@ Degree getModel() { @Override List getAll() { - return TestData.DEGREES; + return DEGREES; } } diff --git a/backend/src/test/java/ch/puzzle/pcts/service/persistence/DegreeTypePersistenceServiceIT.java b/backend/src/test/java/ch/puzzle/pcts/service/persistence/DegreeTypePersistenceServiceIT.java index 576854f85..de6e02b63 100644 --- a/backend/src/test/java/ch/puzzle/pcts/service/persistence/DegreeTypePersistenceServiceIT.java +++ b/backend/src/test/java/ch/puzzle/pcts/service/persistence/DegreeTypePersistenceServiceIT.java @@ -1,8 +1,9 @@ package ch.puzzle.pcts.service.persistence; +import static ch.puzzle.pcts.util.TestData.DEGREE_TYPES; + import ch.puzzle.pcts.model.degreetype.DegreeType; import ch.puzzle.pcts.repository.DegreeTypeRepository; -import ch.puzzle.pcts.util.TestData; import java.math.BigDecimal; import java.util.List; import org.springframework.beans.factory.annotation.Autowired; @@ -27,6 +28,6 @@ DegreeType getModel() { @Override List getAll() { - return TestData.DEGREE_TYPES; + return DEGREE_TYPES; } } \ No newline at end of file diff --git a/backend/src/test/java/ch/puzzle/pcts/service/persistence/ExperiencePersistenceServiceIT.java b/backend/src/test/java/ch/puzzle/pcts/service/persistence/ExperiencePersistenceServiceIT.java index f3da1b286..4855290f2 100644 --- a/backend/src/test/java/ch/puzzle/pcts/service/persistence/ExperiencePersistenceServiceIT.java +++ b/backend/src/test/java/ch/puzzle/pcts/service/persistence/ExperiencePersistenceServiceIT.java @@ -1,8 +1,9 @@ package ch.puzzle.pcts.service.persistence; +import static ch.puzzle.pcts.util.TestData.*; + import ch.puzzle.pcts.model.experience.Experience; import ch.puzzle.pcts.repository.ExperienceRepository; -import ch.puzzle.pcts.util.TestData; import java.time.LocalDate; import java.util.List; import org.springframework.beans.factory.annotation.Autowired; @@ -19,11 +20,11 @@ public class ExperiencePersistenceServiceIT @Override Experience getModel() { return new Experience.Builder() - .withMember(TestData.MEMBER_1) + .withMember(MEMBER_1) .withName("Experience 4") .withEmployer("Employer 4") .withPercent(100) - .withType(TestData.EXP_TYPE_1) + .withType(EXP_TYPE_1) .withComment("Comment test 4") .withStartDate(LocalDate.of(2021, 7, 15)) .withEndDate(LocalDate.of(2022, 7, 15)) @@ -31,6 +32,6 @@ Experience getModel() { } List getAll() { - return TestData.EXPERIENCES; + return EXPERIENCES; } } \ No newline at end of file diff --git a/backend/src/test/java/ch/puzzle/pcts/service/persistence/ExperienceTypePersistenceServiceIT.java b/backend/src/test/java/ch/puzzle/pcts/service/persistence/ExperienceTypePersistenceServiceIT.java index a47ec43b7..2205a8747 100644 --- a/backend/src/test/java/ch/puzzle/pcts/service/persistence/ExperienceTypePersistenceServiceIT.java +++ b/backend/src/test/java/ch/puzzle/pcts/service/persistence/ExperienceTypePersistenceServiceIT.java @@ -1,8 +1,9 @@ package ch.puzzle.pcts.service.persistence; +import static ch.puzzle.pcts.util.TestData.EXPERIENCE_TYPES; + import ch.puzzle.pcts.model.experiencetype.ExperienceType; import ch.puzzle.pcts.repository.ExperienceTypeRepository; -import ch.puzzle.pcts.util.TestData; import java.math.BigDecimal; import java.util.List; import org.springframework.beans.factory.annotation.Autowired; @@ -27,6 +28,6 @@ ExperienceType getModel() { @Override List getAll() { - return TestData.EXPERIENCE_TYPES; + return EXPERIENCE_TYPES; } } diff --git a/backend/src/test/java/ch/puzzle/pcts/service/persistence/LeadershipTypePersistenceServiceIT.java b/backend/src/test/java/ch/puzzle/pcts/service/persistence/LeadershipTypePersistenceServiceIT.java index f829326a3..aee91544f 100644 --- a/backend/src/test/java/ch/puzzle/pcts/service/persistence/LeadershipTypePersistenceServiceIT.java +++ b/backend/src/test/java/ch/puzzle/pcts/service/persistence/LeadershipTypePersistenceServiceIT.java @@ -1,6 +1,7 @@ package ch.puzzle.pcts.service.persistence; import static ch.puzzle.pcts.Constants.LEADERSHIP_EXPERIENCE_TYPE; +import static ch.puzzle.pcts.util.TestData.LEADERSHIP_EXPERIENCE_TYPES; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatThrownBy; import static org.junit.jupiter.api.Assertions.*; @@ -12,7 +13,6 @@ import ch.puzzle.pcts.model.certificatetype.CertificateType; import ch.puzzle.pcts.model.certificatetype.Tag; import ch.puzzle.pcts.repository.CertificateTypeRepository; -import ch.puzzle.pcts.util.TestData; import java.math.BigDecimal; import java.util.List; import java.util.Map; @@ -43,7 +43,7 @@ CertificateType getModel() { @Override List getAll() { - return TestData.LEADERSHIP_EXPERIENCE_TYPES; + return LEADERSHIP_EXPERIENCE_TYPES; } @Override diff --git a/backend/src/test/java/ch/puzzle/pcts/service/persistence/MemberOverviewPersistenceServiceIT.java b/backend/src/test/java/ch/puzzle/pcts/service/persistence/MemberOverviewPersistenceServiceIT.java index 907eab245..18f0246ad 100644 --- a/backend/src/test/java/ch/puzzle/pcts/service/persistence/MemberOverviewPersistenceServiceIT.java +++ b/backend/src/test/java/ch/puzzle/pcts/service/persistence/MemberOverviewPersistenceServiceIT.java @@ -1,9 +1,9 @@ package ch.puzzle.pcts.service.persistence; +import static ch.puzzle.pcts.util.TestData.MEMBER_1_OVERVIEWS; import static org.assertj.core.api.Assertions.assertThat; import ch.puzzle.pcts.model.memberoverview.MemberOverview; -import ch.puzzle.pcts.util.TestData; import java.util.List; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; @@ -46,6 +46,6 @@ void shouldGetAllMemberOverviewRowsForMember1() { assertThat(memberOverviews) .usingRecursiveFieldByFieldElementComparator() - .containsExactlyElementsOf(TestData.MEMBER_1_OVERVIEWS); + .containsExactlyElementsOf(MEMBER_1_OVERVIEWS); } } diff --git a/backend/src/test/java/ch/puzzle/pcts/service/persistence/MemberPersistenceServiceIT.java b/backend/src/test/java/ch/puzzle/pcts/service/persistence/MemberPersistenceServiceIT.java index 63962d25c..e530e429c 100644 --- a/backend/src/test/java/ch/puzzle/pcts/service/persistence/MemberPersistenceServiceIT.java +++ b/backend/src/test/java/ch/puzzle/pcts/service/persistence/MemberPersistenceServiceIT.java @@ -1,9 +1,11 @@ package ch.puzzle.pcts.service.persistence; +import static ch.puzzle.pcts.util.TestData.MEMBERS; +import static ch.puzzle.pcts.util.TestData.ORG_UNIT_2; + import ch.puzzle.pcts.model.member.EmploymentState; import ch.puzzle.pcts.model.member.Member; import ch.puzzle.pcts.repository.MemberRepository; -import ch.puzzle.pcts.util.TestData; import java.time.LocalDate; import java.util.List; import org.springframework.beans.factory.annotation.Autowired; @@ -26,12 +28,12 @@ Member getModel() { .withAbbreviation("M1") .withDateOfHire(LocalDate.of(2019, 8, 4)) .withBirthDate(LocalDate.of(1970, 1, 1)) - .withOrganisationUnit(TestData.ORG_UNIT_2) + .withOrganisationUnit(ORG_UNIT_2) .build(); } @Override List getAll() { - return TestData.MEMBERS; + return MEMBERS; } } diff --git a/backend/src/test/java/ch/puzzle/pcts/service/persistence/OrganisationUnitPersistenceServiceIT.java b/backend/src/test/java/ch/puzzle/pcts/service/persistence/OrganisationUnitPersistenceServiceIT.java index f3581ba31..8a963b4c2 100644 --- a/backend/src/test/java/ch/puzzle/pcts/service/persistence/OrganisationUnitPersistenceServiceIT.java +++ b/backend/src/test/java/ch/puzzle/pcts/service/persistence/OrganisationUnitPersistenceServiceIT.java @@ -1,10 +1,10 @@ package ch.puzzle.pcts.service.persistence; +import static ch.puzzle.pcts.util.TestData.ORGANISATION_UNITS; import static org.assertj.core.api.Assertions.assertThat; import ch.puzzle.pcts.model.organisationunit.OrganisationUnit; import ch.puzzle.pcts.repository.OrganisationUnitRepository; -import ch.puzzle.pcts.util.TestData; import java.util.List; import java.util.Optional; import org.junit.jupiter.api.DisplayName; @@ -30,7 +30,7 @@ OrganisationUnit getModel() { @Override List getAll() { - return TestData.ORGANISATION_UNITS; + return ORGANISATION_UNITS; } @DisplayName("Should get organisationUnit by name") diff --git a/backend/src/test/java/ch/puzzle/pcts/service/persistence/RolePersistenceServiceIT.java b/backend/src/test/java/ch/puzzle/pcts/service/persistence/RolePersistenceServiceIT.java index 20bec7d48..86179b286 100644 --- a/backend/src/test/java/ch/puzzle/pcts/service/persistence/RolePersistenceServiceIT.java +++ b/backend/src/test/java/ch/puzzle/pcts/service/persistence/RolePersistenceServiceIT.java @@ -1,10 +1,10 @@ package ch.puzzle.pcts.service.persistence; +import static ch.puzzle.pcts.util.TestData.ROLES; import static org.assertj.core.api.Assertions.assertThat; import ch.puzzle.pcts.model.role.Role; import ch.puzzle.pcts.repository.RoleRepository; -import ch.puzzle.pcts.util.TestData; import java.util.List; import java.util.Optional; import org.junit.jupiter.api.DisplayName; @@ -27,7 +27,7 @@ Role getModel() { } List getAll() { - return TestData.ROLES; + return ROLES; } @DisplayName("Should get role by name") diff --git a/backend/src/test/java/ch/puzzle/pcts/service/persistence/TagPersistenceServiceIT.java b/backend/src/test/java/ch/puzzle/pcts/service/persistence/TagPersistenceServiceIT.java index 9785c59e0..6db4a7fd9 100644 --- a/backend/src/test/java/ch/puzzle/pcts/service/persistence/TagPersistenceServiceIT.java +++ b/backend/src/test/java/ch/puzzle/pcts/service/persistence/TagPersistenceServiceIT.java @@ -1,10 +1,10 @@ package ch.puzzle.pcts.service.persistence; +import static ch.puzzle.pcts.util.TestData.TAGS; import static org.assertj.core.api.Assertions.assertThat; import ch.puzzle.pcts.model.certificatetype.Tag; import ch.puzzle.pcts.repository.TagRepository; -import ch.puzzle.pcts.util.TestData; import jakarta.transaction.Transactional; import java.util.List; import org.junit.jupiter.api.DisplayName; @@ -28,7 +28,7 @@ Tag getModel() { } List getAll() { - return TestData.TAGS; + return TAGS; } @DisplayName("Should find tag by name written in different cases") diff --git a/backend/src/test/java/ch/puzzle/pcts/util/TestData.java b/backend/src/test/java/ch/puzzle/pcts/util/TestData.java index 0ef16626d..f28fb7ece 100644 --- a/backend/src/test/java/ch/puzzle/pcts/util/TestData.java +++ b/backend/src/test/java/ch/puzzle/pcts/util/TestData.java @@ -256,225 +256,225 @@ private TestData() { .of(MemberOverview.Builder .builder() .withUniqueRowId(1L) - .withMemberId(TestData.MEMBER_1.getId()) - .withFirstName(TestData.MEMBER_1.getFirstName()) - .withLastName(TestData.MEMBER_1.getLastName()) - .withAbbreviation(TestData.MEMBER_1.getAbbreviation()) - .withEmploymentState(TestData.MEMBER_1.getEmploymentState()) - .withDateOfHire(TestData.MEMBER_1.getDateOfHire()) - .withBirthDate(TestData.MEMBER_1.getBirthDate()) - .withOrganisationUnitName(TestData.MEMBER_1.getOrganisationUnit().getName()) - .withCertificateId(TestData.CERTIFICATE_1.getId()) - .withCertificateCompletedAt(TestData.CERTIFICATE_1.getCompletedAt()) - .withCertificateValidUntil(TestData.CERTIFICATE_1.getValidUntil()) - .withCertificateComment(TestData.CERTIFICATE_1.getComment()) - .withCertificateTypeName(TestData.CERTIFICATE_1.getCertificateType().getName()) - .withCertificateTypeComment(TestData.CERTIFICATE_1.getCertificateType().getComment()) + .withMemberId(MEMBER_1.getId()) + .withFirstName(MEMBER_1.getFirstName()) + .withLastName(MEMBER_1.getLastName()) + .withAbbreviation(MEMBER_1.getAbbreviation()) + .withEmploymentState(MEMBER_1.getEmploymentState()) + .withDateOfHire(MEMBER_1.getDateOfHire()) + .withBirthDate(MEMBER_1.getBirthDate()) + .withOrganisationUnitName(MEMBER_1.getOrganisationUnit().getName()) + .withCertificateId(CERTIFICATE_1.getId()) + .withCertificateCompletedAt(CERTIFICATE_1.getCompletedAt()) + .withCertificateValidUntil(CERTIFICATE_1.getValidUntil()) + .withCertificateComment(CERTIFICATE_1.getComment()) + .withCertificateTypeName(CERTIFICATE_1.getCertificateType().getName()) + .withCertificateTypeComment(CERTIFICATE_1.getCertificateType().getComment()) .withleadershipTypeKind(CertificateKind.CERTIFICATE) - .withDegreeId(TestData.DEGREE_1.getId()) - .withDegreeName(TestData.DEGREE_1.getName()) - .withDegreeStartDate(TestData.DEGREE_1.getStartDate()) - .withDegreeEndDate(TestData.DEGREE_1.getEndDate()) - .withDegreeComment(TestData.DEGREE_1.getComment()) - .withDegreeTypeName(TestData.DEGREE_1.getDegreeType().getName()) - .withExperienceId(TestData.EXPERIENCE_1.getId()) - .withExperienceName(TestData.EXPERIENCE_1.getName()) - .withExperienceEmployer(TestData.EXPERIENCE_1.getEmployer()) - .withExperienceStartDate(TestData.EXPERIENCE_1.getStartDate()) - .withExperienceEndDate(TestData.EXPERIENCE_1.getEndDate()) - .withExperienceComment(TestData.EXPERIENCE_1.getComment()) - .withExperienceTypeName(TestData.EXPERIENCE_1.getType().getName()) + .withDegreeId(DEGREE_1.getId()) + .withDegreeName(DEGREE_1.getName()) + .withDegreeStartDate(DEGREE_1.getStartDate()) + .withDegreeEndDate(DEGREE_1.getEndDate()) + .withDegreeComment(DEGREE_1.getComment()) + .withDegreeTypeName(DEGREE_1.getDegreeType().getName()) + .withExperienceId(EXPERIENCE_1.getId()) + .withExperienceName(EXPERIENCE_1.getName()) + .withExperienceEmployer(EXPERIENCE_1.getEmployer()) + .withExperienceStartDate(EXPERIENCE_1.getStartDate()) + .withExperienceEndDate(EXPERIENCE_1.getEndDate()) + .withExperienceComment(EXPERIENCE_1.getComment()) + .withExperienceTypeName(EXPERIENCE_1.getType().getName()) .build(), MemberOverview.Builder .builder() .withUniqueRowId(2L) - .withMemberId(TestData.MEMBER_1.getId()) - .withFirstName(TestData.MEMBER_1.getFirstName()) - .withLastName(TestData.MEMBER_1.getLastName()) - .withAbbreviation(TestData.MEMBER_1.getAbbreviation()) - .withEmploymentState(TestData.MEMBER_1.getEmploymentState()) - .withDateOfHire(TestData.MEMBER_1.getDateOfHire()) - .withBirthDate(TestData.MEMBER_1.getBirthDate()) - .withOrganisationUnitName(TestData.MEMBER_1.getOrganisationUnit().getName()) - .withCertificateId(TestData.CERTIFICATE_1.getId()) - .withCertificateCompletedAt(TestData.CERTIFICATE_1.getCompletedAt()) - .withCertificateValidUntil(TestData.CERTIFICATE_1.getValidUntil()) - .withCertificateComment(TestData.CERTIFICATE_1.getComment()) - .withCertificateTypeName(TestData.CERTIFICATE_1.getCertificateType().getName()) - .withCertificateTypeComment(TestData.CERTIFICATE_1.getCertificateType().getComment()) + .withMemberId(MEMBER_1.getId()) + .withFirstName(MEMBER_1.getFirstName()) + .withLastName(MEMBER_1.getLastName()) + .withAbbreviation(MEMBER_1.getAbbreviation()) + .withEmploymentState(MEMBER_1.getEmploymentState()) + .withDateOfHire(MEMBER_1.getDateOfHire()) + .withBirthDate(MEMBER_1.getBirthDate()) + .withOrganisationUnitName(MEMBER_1.getOrganisationUnit().getName()) + .withCertificateId(CERTIFICATE_1.getId()) + .withCertificateCompletedAt(CERTIFICATE_1.getCompletedAt()) + .withCertificateValidUntil(CERTIFICATE_1.getValidUntil()) + .withCertificateComment(CERTIFICATE_1.getComment()) + .withCertificateTypeName(CERTIFICATE_1.getCertificateType().getName()) + .withCertificateTypeComment(CERTIFICATE_1.getCertificateType().getComment()) .withleadershipTypeKind(CertificateKind.CERTIFICATE) - .withDegreeId(TestData.DEGREE_1.getId()) - .withDegreeName(TestData.DEGREE_1.getName()) - .withDegreeStartDate(TestData.DEGREE_1.getStartDate()) - .withDegreeEndDate(TestData.DEGREE_1.getEndDate()) - .withDegreeComment(TestData.DEGREE_1.getComment()) - .withDegreeTypeName(TestData.DEGREE_1.getDegreeType().getName()) - .withExperienceId(TestData.EXPERIENCE_2.getId()) - .withExperienceName(TestData.EXPERIENCE_2.getName()) - .withExperienceEmployer(TestData.EXPERIENCE_2.getEmployer()) - .withExperienceStartDate(TestData.EXPERIENCE_2.getStartDate()) - .withExperienceEndDate(TestData.EXPERIENCE_2.getEndDate()) - .withExperienceComment(TestData.EXPERIENCE_2.getComment()) - .withExperienceTypeName(TestData.EXPERIENCE_2.getType().getName()) + .withDegreeId(DEGREE_1.getId()) + .withDegreeName(DEGREE_1.getName()) + .withDegreeStartDate(DEGREE_1.getStartDate()) + .withDegreeEndDate(DEGREE_1.getEndDate()) + .withDegreeComment(DEGREE_1.getComment()) + .withDegreeTypeName(DEGREE_1.getDegreeType().getName()) + .withExperienceId(EXPERIENCE_2.getId()) + .withExperienceName(EXPERIENCE_2.getName()) + .withExperienceEmployer(EXPERIENCE_2.getEmployer()) + .withExperienceStartDate(EXPERIENCE_2.getStartDate()) + .withExperienceEndDate(EXPERIENCE_2.getEndDate()) + .withExperienceComment(EXPERIENCE_2.getComment()) + .withExperienceTypeName(EXPERIENCE_2.getType().getName()) .build(), MemberOverview.Builder .builder() .withUniqueRowId(3L) - .withMemberId(TestData.MEMBER_1.getId()) - .withFirstName(TestData.MEMBER_1.getFirstName()) - .withLastName(TestData.MEMBER_1.getLastName()) - .withAbbreviation(TestData.MEMBER_1.getAbbreviation()) - .withEmploymentState(TestData.MEMBER_1.getEmploymentState()) - .withDateOfHire(TestData.MEMBER_1.getDateOfHire()) - .withBirthDate(TestData.MEMBER_1.getBirthDate()) - .withOrganisationUnitName(TestData.MEMBER_1.getOrganisationUnit().getName()) - .withCertificateId(TestData.CERTIFICATE_4.getId()) - .withCertificateCompletedAt(TestData.CERTIFICATE_4.getCompletedAt()) - .withCertificateValidUntil(TestData.CERTIFICATE_4.getValidUntil()) - .withCertificateComment(TestData.CERTIFICATE_4.getComment()) - .withCertificateTypeName(TestData.CERTIFICATE_4.getCertificateType().getName()) - .withCertificateTypeComment(TestData.CERTIFICATE_4.getCertificateType().getComment()) + .withMemberId(MEMBER_1.getId()) + .withFirstName(MEMBER_1.getFirstName()) + .withLastName(MEMBER_1.getLastName()) + .withAbbreviation(MEMBER_1.getAbbreviation()) + .withEmploymentState(MEMBER_1.getEmploymentState()) + .withDateOfHire(MEMBER_1.getDateOfHire()) + .withBirthDate(MEMBER_1.getBirthDate()) + .withOrganisationUnitName(MEMBER_1.getOrganisationUnit().getName()) + .withCertificateId(CERTIFICATE_4.getId()) + .withCertificateCompletedAt(CERTIFICATE_4.getCompletedAt()) + .withCertificateValidUntil(CERTIFICATE_4.getValidUntil()) + .withCertificateComment(CERTIFICATE_4.getComment()) + .withCertificateTypeName(CERTIFICATE_4.getCertificateType().getName()) + .withCertificateTypeComment(CERTIFICATE_4.getCertificateType().getComment()) .withleadershipTypeKind(CertificateKind.CERTIFICATE) - .withDegreeId(TestData.DEGREE_1.getId()) - .withDegreeName(TestData.DEGREE_1.getName()) - .withDegreeStartDate(TestData.DEGREE_1.getStartDate()) - .withDegreeEndDate(TestData.DEGREE_1.getEndDate()) - .withDegreeComment(TestData.DEGREE_1.getComment()) - .withDegreeTypeName(TestData.DEGREE_1.getDegreeType().getName()) - .withExperienceId(TestData.EXPERIENCE_1.getId()) - .withExperienceName(TestData.EXPERIENCE_1.getName()) - .withExperienceEmployer(TestData.EXPERIENCE_1.getEmployer()) - .withExperienceStartDate(TestData.EXPERIENCE_1.getStartDate()) - .withExperienceEndDate(TestData.EXPERIENCE_1.getEndDate()) - .withExperienceComment(TestData.EXPERIENCE_1.getComment()) - .withExperienceTypeName(TestData.EXPERIENCE_1.getType().getName()) + .withDegreeId(DEGREE_1.getId()) + .withDegreeName(DEGREE_1.getName()) + .withDegreeStartDate(DEGREE_1.getStartDate()) + .withDegreeEndDate(DEGREE_1.getEndDate()) + .withDegreeComment(DEGREE_1.getComment()) + .withDegreeTypeName(DEGREE_1.getDegreeType().getName()) + .withExperienceId(EXPERIENCE_1.getId()) + .withExperienceName(EXPERIENCE_1.getName()) + .withExperienceEmployer(EXPERIENCE_1.getEmployer()) + .withExperienceStartDate(EXPERIENCE_1.getStartDate()) + .withExperienceEndDate(EXPERIENCE_1.getEndDate()) + .withExperienceComment(EXPERIENCE_1.getComment()) + .withExperienceTypeName(EXPERIENCE_1.getType().getName()) .build(), MemberOverview.Builder .builder() .withUniqueRowId(4L) - .withMemberId(TestData.MEMBER_1.getId()) - .withFirstName(TestData.MEMBER_1.getFirstName()) - .withLastName(TestData.MEMBER_1.getLastName()) - .withAbbreviation(TestData.MEMBER_1.getAbbreviation()) - .withEmploymentState(TestData.MEMBER_1.getEmploymentState()) - .withDateOfHire(TestData.MEMBER_1.getDateOfHire()) - .withBirthDate(TestData.MEMBER_1.getBirthDate()) - .withOrganisationUnitName(TestData.MEMBER_1.getOrganisationUnit().getName()) - .withCertificateId(TestData.CERTIFICATE_4.getId()) - .withCertificateCompletedAt(TestData.CERTIFICATE_4.getCompletedAt()) - .withCertificateValidUntil(TestData.CERTIFICATE_4.getValidUntil()) - .withCertificateComment(TestData.CERTIFICATE_4.getComment()) - .withCertificateTypeName(TestData.CERTIFICATE_4.getCertificateType().getName()) - .withCertificateTypeComment(TestData.CERTIFICATE_4.getCertificateType().getComment()) + .withMemberId(MEMBER_1.getId()) + .withFirstName(MEMBER_1.getFirstName()) + .withLastName(MEMBER_1.getLastName()) + .withAbbreviation(MEMBER_1.getAbbreviation()) + .withEmploymentState(MEMBER_1.getEmploymentState()) + .withDateOfHire(MEMBER_1.getDateOfHire()) + .withBirthDate(MEMBER_1.getBirthDate()) + .withOrganisationUnitName(MEMBER_1.getOrganisationUnit().getName()) + .withCertificateId(CERTIFICATE_4.getId()) + .withCertificateCompletedAt(CERTIFICATE_4.getCompletedAt()) + .withCertificateValidUntil(CERTIFICATE_4.getValidUntil()) + .withCertificateComment(CERTIFICATE_4.getComment()) + .withCertificateTypeName(CERTIFICATE_4.getCertificateType().getName()) + .withCertificateTypeComment(CERTIFICATE_4.getCertificateType().getComment()) .withleadershipTypeKind(CertificateKind.CERTIFICATE) - .withDegreeId(TestData.DEGREE_1.getId()) - .withDegreeName(TestData.DEGREE_1.getName()) - .withDegreeStartDate(TestData.DEGREE_1.getStartDate()) - .withDegreeEndDate(TestData.DEGREE_1.getEndDate()) - .withDegreeComment(TestData.DEGREE_1.getComment()) - .withDegreeTypeName(TestData.DEGREE_1.getDegreeType().getName()) - .withExperienceId(TestData.EXPERIENCE_2.getId()) - .withExperienceName(TestData.EXPERIENCE_2.getName()) - .withExperienceEmployer(TestData.EXPERIENCE_2.getEmployer()) - .withExperienceStartDate(TestData.EXPERIENCE_2.getStartDate()) - .withExperienceEndDate(TestData.EXPERIENCE_2.getEndDate()) - .withExperienceComment(TestData.EXPERIENCE_2.getComment()) - .withExperienceTypeName(TestData.EXPERIENCE_2.getType().getName()) + .withDegreeId(DEGREE_1.getId()) + .withDegreeName(DEGREE_1.getName()) + .withDegreeStartDate(DEGREE_1.getStartDate()) + .withDegreeEndDate(DEGREE_1.getEndDate()) + .withDegreeComment(DEGREE_1.getComment()) + .withDegreeTypeName(DEGREE_1.getDegreeType().getName()) + .withExperienceId(EXPERIENCE_2.getId()) + .withExperienceName(EXPERIENCE_2.getName()) + .withExperienceEmployer(EXPERIENCE_2.getEmployer()) + .withExperienceStartDate(EXPERIENCE_2.getStartDate()) + .withExperienceEndDate(EXPERIENCE_2.getEndDate()) + .withExperienceComment(EXPERIENCE_2.getComment()) + .withExperienceTypeName(EXPERIENCE_2.getType().getName()) .build()); - public static final MemberOverviewDto MEMBER_1_OVERVIEW_DTO = new MemberOverviewDto(new MemberOverviewMemberDto(TestData.MEMBER_1 + public static final MemberOverviewDto MEMBER_1_OVERVIEW_DTO = new MemberOverviewDto(new MemberOverviewMemberDto(MEMBER_1 .getId(), - TestData.MEMBER_1 + MEMBER_1 .getFirstName(), - TestData.MEMBER_1 + MEMBER_1 .getLastName(), - TestData.MEMBER_1 + MEMBER_1 .getEmploymentState(), - TestData.MEMBER_1 + MEMBER_1 .getAbbreviation(), - TestData.MEMBER_1 + MEMBER_1 .getDateOfHire(), - TestData.MEMBER_1 + MEMBER_1 .getBirthDate(), - TestData.MEMBER_1 + MEMBER_1 .getOrganisationUnit() .getName()), new MemberCvDto(List - .of(new MemberOverviewDegreeDto(TestData.DEGREE_1 + .of(new MemberOverviewDegreeDto(DEGREE_1 .getId(), - TestData.DEGREE_1 + DEGREE_1 .getName(), - new MemberOverviewDegreeTypeDto(TestData.DEGREE_1 + new MemberOverviewDegreeTypeDto(DEGREE_1 .getDegreeType() .getName()), - TestData.DEGREE_1 + DEGREE_1 .getStartDate(), - TestData.DEGREE_1 + DEGREE_1 .getEndDate(), - TestData.DEGREE_1 + DEGREE_1 .getComment())), List - .of(new MemberOverviewExperienceDto(TestData.EXPERIENCE_1 + .of(new MemberOverviewExperienceDto(EXPERIENCE_1 .getId(), - TestData.EXPERIENCE_1 + EXPERIENCE_1 .getName(), - TestData.EXPERIENCE_1 + EXPERIENCE_1 .getEmployer(), - new MemberOverviewExperienceTypeDto(TestData.EXPERIENCE_1 + new MemberOverviewExperienceTypeDto(EXPERIENCE_1 .getType() .getName()), - TestData.EXPERIENCE_1 + EXPERIENCE_1 .getComment(), - TestData.EXPERIENCE_1 + EXPERIENCE_1 .getStartDate(), - TestData.EXPERIENCE_1 + EXPERIENCE_1 .getEndDate()), - new MemberOverviewExperienceDto(TestData.EXPERIENCE_2 + new MemberOverviewExperienceDto(EXPERIENCE_2 .getId(), - TestData.EXPERIENCE_2 + EXPERIENCE_2 .getName(), - TestData.EXPERIENCE_2 + EXPERIENCE_2 .getEmployer(), - new MemberOverviewExperienceTypeDto(TestData.EXPERIENCE_2 + new MemberOverviewExperienceTypeDto(EXPERIENCE_2 .getType() .getName()), - TestData.EXPERIENCE_2 + EXPERIENCE_2 .getComment(), - TestData.EXPERIENCE_2 + EXPERIENCE_2 .getStartDate(), - TestData.EXPERIENCE_2 + EXPERIENCE_2 .getEndDate())), List - .of(new MemberOverviewCertificateDto(TestData.CERTIFICATE_1 + .of(new MemberOverviewCertificateDto(CERTIFICATE_1 .getId(), - new MemberOverviewCertificateTypeDto(TestData.CERTIFICATE_1 + new MemberOverviewCertificateTypeDto(CERTIFICATE_1 .getCertificateType() .getName(), - TestData.CERTIFICATE_1 + CERTIFICATE_1 .getCertificateType() .getComment()), - TestData.CERTIFICATE_1 + CERTIFICATE_1 .getCompletedAt(), - TestData.CERTIFICATE_1 + CERTIFICATE_1 .getValidUntil(), - TestData.CERTIFICATE_1 + CERTIFICATE_1 .getComment()), - new MemberOverviewCertificateDto(TestData.CERTIFICATE_4 + new MemberOverviewCertificateDto(CERTIFICATE_4 .getId(), - new MemberOverviewCertificateTypeDto(TestData.CERTIFICATE_4 + new MemberOverviewCertificateTypeDto(CERTIFICATE_4 .getCertificateType() .getName(), - TestData.CERTIFICATE_4 + CERTIFICATE_4 .getCertificateType() .getComment()), - TestData.CERTIFICATE_4 + CERTIFICATE_4 .getCompletedAt(), - TestData.CERTIFICATE_4 + CERTIFICATE_4 .getValidUntil(), - TestData.CERTIFICATE_4 + CERTIFICATE_4 .getComment())), List.of())); From cb020b1007cdbba5fdff1581da7d3016f6f2cea3 Mon Sep 17 00:00:00 2001 From: Manuel Date: Wed, 7 Jan 2026 13:49:37 +0100 Subject: [PATCH 15/30] refactor(memberoverview): Remove unwanted attributes and make small quality of life improvements #50 --- .../controller/MemberOverviewController.java | 3 +- .../MemberOverviewCertificateDto.java | 1 - .../MemberOverviewCertificateTypeDto.java | 3 +- .../degree/MemberOverviewDegreeDto.java | 4 +- ...erOverviewLeadershipExperienceTypeDto.java | 1 - .../pcts/mapper/MemberOverviewMapper.java | 140 ++++++++---------- .../model/memberoverview/MemberOverview.java | 48 ------ .../MemberOverviewBusinessService.java | 24 ++- 8 files changed, 87 insertions(+), 137 deletions(-) diff --git a/backend/src/main/java/ch/puzzle/pcts/controller/MemberOverviewController.java b/backend/src/main/java/ch/puzzle/pcts/controller/MemberOverviewController.java index b26bf7b0c..25639ecd8 100644 --- a/backend/src/main/java/ch/puzzle/pcts/controller/MemberOverviewController.java +++ b/backend/src/main/java/ch/puzzle/pcts/controller/MemberOverviewController.java @@ -1,6 +1,5 @@ package ch.puzzle.pcts.controller; -import ch.puzzle.pcts.dto.member.MemberDto; import ch.puzzle.pcts.dto.memberoverview.MemberOverviewDto; import ch.puzzle.pcts.mapper.MemberOverviewMapper; import ch.puzzle.pcts.model.memberoverview.MemberOverview; @@ -32,7 +31,7 @@ public MemberOverviewController(MemberOverviewMapper mapper, MemberOverviewBusin @Operation(summary = "Get the member-overview by memberId") @ApiResponse(responseCode = "200", description = "Successfully retrieved the member-overview.", content = { - @Content(mediaType = "application/json", schema = @Schema(implementation = MemberDto.class)) }) + @Content(mediaType = "application/json", schema = @Schema(implementation = MemberOverviewDto.class)) }) @GetMapping("{memberId}") public ResponseEntity getMemberOverviewByMemberId(@Parameter(description = "The ID of the member whose overview should be retrieved.", required = true) @PathVariable Long memberId) { diff --git a/backend/src/main/java/ch/puzzle/pcts/dto/memberoverview/certificate/MemberOverviewCertificateDto.java b/backend/src/main/java/ch/puzzle/pcts/dto/memberoverview/certificate/MemberOverviewCertificateDto.java index a48aa7842..6e85f96eb 100644 --- a/backend/src/main/java/ch/puzzle/pcts/dto/memberoverview/certificate/MemberOverviewCertificateDto.java +++ b/backend/src/main/java/ch/puzzle/pcts/dto/memberoverview/certificate/MemberOverviewCertificateDto.java @@ -7,6 +7,5 @@ public record MemberOverviewCertificateDto( @Schema(description = "The unique identifier of the member certificate.", example = "1", requiredMode = Schema.RequiredMode.REQUIRED, accessMode = Schema.AccessMode.READ_ONLY, nullable = false) Long id, @Schema(description = "The type of certificate awarded to the member.", exampleClasses = MemberOverviewCertificateTypeDto.class, requiredMode = Schema.RequiredMode.REQUIRED, nullable = false) MemberOverviewCertificateTypeDto certificate, @Schema(description = "The date when the member completed the certificate.", example = "2025-09-24", requiredMode = Schema.RequiredMode.NOT_REQUIRED, nullable = true) LocalDate completedAt, - @Schema(description = "The date until which the certificate is valid.", example = "2028-02-12", requiredMode = Schema.RequiredMode.NOT_REQUIRED, nullable = true) LocalDate validUntil, @Schema(description = "An optional comment for the member certificate.", example = "Completed via fast-track program", requiredMode = Schema.RequiredMode.NOT_REQUIRED, nullable = true) String comment) { } diff --git a/backend/src/main/java/ch/puzzle/pcts/dto/memberoverview/certificate/MemberOverviewCertificateTypeDto.java b/backend/src/main/java/ch/puzzle/pcts/dto/memberoverview/certificate/MemberOverviewCertificateTypeDto.java index b7993d570..31116763b 100644 --- a/backend/src/main/java/ch/puzzle/pcts/dto/memberoverview/certificate/MemberOverviewCertificateTypeDto.java +++ b/backend/src/main/java/ch/puzzle/pcts/dto/memberoverview/certificate/MemberOverviewCertificateTypeDto.java @@ -3,6 +3,5 @@ import io.swagger.v3.oas.annotations.media.Schema; public record MemberOverviewCertificateTypeDto( - @Schema(description = "The name of the certificate-type.", example = "Certified GitOps Associate", requiredMode = Schema.RequiredMode.REQUIRED, nullable = false, minLength = 1) String name, - @Schema(description = "An optional comment for the certificate-type.", example = "This is an awesome certificate-type.", requiredMode = Schema.RequiredMode.NOT_REQUIRED, nullable = true) String comment) { + @Schema(description = "The name of the certificate-type.", example = "Certified GitOps Associate", requiredMode = Schema.RequiredMode.REQUIRED, nullable = false, minLength = 1) String name) { } diff --git a/backend/src/main/java/ch/puzzle/pcts/dto/memberoverview/degree/MemberOverviewDegreeDto.java b/backend/src/main/java/ch/puzzle/pcts/dto/memberoverview/degree/MemberOverviewDegreeDto.java index df51a9466..f5de5febe 100644 --- a/backend/src/main/java/ch/puzzle/pcts/dto/memberoverview/degree/MemberOverviewDegreeDto.java +++ b/backend/src/main/java/ch/puzzle/pcts/dto/memberoverview/degree/MemberOverviewDegreeDto.java @@ -12,7 +12,5 @@ public record MemberOverviewDegreeDto( @Schema(description = "The start date of the degree program.", example = "2018-09-01", requiredMode = Schema.RequiredMode.REQUIRED) LocalDate startDate, - @Schema(description = "The end date of the degree program.", example = "2022-06-30") LocalDate endDate, - - @Schema(description = "Additional comments or notes about the degree.", example = "Graduated summa cum laude.") String comment) { + @Schema(description = "The end date of the degree program.", example = "2022-06-30") LocalDate endDate) { } diff --git a/backend/src/main/java/ch/puzzle/pcts/dto/memberoverview/leadershipexperience/MemberOverviewLeadershipExperienceTypeDto.java b/backend/src/main/java/ch/puzzle/pcts/dto/memberoverview/leadershipexperience/MemberOverviewLeadershipExperienceTypeDto.java index d79108ee2..2743b09d4 100644 --- a/backend/src/main/java/ch/puzzle/pcts/dto/memberoverview/leadershipexperience/MemberOverviewLeadershipExperienceTypeDto.java +++ b/backend/src/main/java/ch/puzzle/pcts/dto/memberoverview/leadershipexperience/MemberOverviewLeadershipExperienceTypeDto.java @@ -5,6 +5,5 @@ public record MemberOverviewLeadershipExperienceTypeDto( @Schema(description = "The name of the leadership-experience-type.", example = "", requiredMode = Schema.RequiredMode.REQUIRED, minLength = 1) String name, - @Schema(description = "A optional comment for the leadership-experience-type.", example = "This is an awesome leadership-experience-type!", requiredMode = Schema.RequiredMode.NOT_REQUIRED, nullable = true) String comment, @Schema(description = "The kind of leadership-experience-type which is either MILITARY_FUNCTION, YOUTH_AND_SPORT or LEADERSHIP_TRAINING", example = "LEADERSHIP_TRAINING", requiredMode = Schema.RequiredMode.REQUIRED) CertificateKind experienceKind) { } diff --git a/backend/src/main/java/ch/puzzle/pcts/mapper/MemberOverviewMapper.java b/backend/src/main/java/ch/puzzle/pcts/mapper/MemberOverviewMapper.java index d680719d2..1c3fe875f 100644 --- a/backend/src/main/java/ch/puzzle/pcts/mapper/MemberOverviewMapper.java +++ b/backend/src/main/java/ch/puzzle/pcts/mapper/MemberOverviewMapper.java @@ -9,105 +9,55 @@ import ch.puzzle.pcts.dto.memberoverview.experience.MemberOverviewExperienceTypeDto; import ch.puzzle.pcts.dto.memberoverview.leadershipexperience.MemberOverviewLeadershipExperienceDto; import ch.puzzle.pcts.dto.memberoverview.leadershipexperience.MemberOverviewLeadershipExperienceTypeDto; -import ch.puzzle.pcts.model.certificatetype.CertificateKind; import ch.puzzle.pcts.model.memberoverview.MemberOverview; +import java.util.ArrayList; +import java.util.HashMap; import java.util.List; -import java.util.stream.Collectors; +import java.util.Map; import org.springframework.stereotype.Component; @Component public class MemberOverviewMapper { public MemberOverviewDto toDto(List memberOverviews) { - if (memberOverviews == null || memberOverviews.isEmpty()) { return null; } - MemberOverviewMemberDto memberDto = getMemberDto(memberOverviews); + Map degreeMap = new HashMap<>(); + Map experienceMap = new HashMap<>(); + Map certificateMap = new HashMap<>(); + Map leadershipMap = new HashMap<>(); - List degrees = memberOverviews - .stream() - .filter(e -> e.getDegreeId() != null) - .collect(Collectors.groupingBy(MemberOverview::getDegreeId)) - .values() - .stream() - .map(group -> { - MemberOverview e = group.getFirst(); - return new MemberOverviewDegreeDto(e.getDegreeId(), - e.getDegreeName(), - new MemberOverviewDegreeTypeDto(e.getDegreeTypeName()), - e.getDegreeStartDate(), - e.getDegreeEndDate(), - e.getDegreeComment()); - }) - .toList(); + for (MemberOverview row : memberOverviews) { - List experiences = memberOverviews - .stream() - .filter(e -> e.getExperienceId() != null) - .collect(Collectors.groupingBy(MemberOverview::getExperienceId)) - .values() - .stream() - .map(group -> { - MemberOverview e = group.getFirst(); - return new MemberOverviewExperienceDto(e.getExperienceId(), - e.getExperienceName(), - e.getExperienceEmployer(), - new MemberOverviewExperienceTypeDto(e - .getExperienceTypeName()), - e.getExperienceComment(), - e.getExperienceStartDate(), - e.getExperienceEndDate()); - }) - .toList(); + if (row.getDegreeId() != null) { + degreeMap.putIfAbsent(row.getDegreeId(), mapToDegree(row)); + } - List certificates = memberOverviews - .stream() - .filter(e -> e.getCertificateId() != null && CertificateKind.CERTIFICATE == e.getLeadershipTypeKind()) - .collect(Collectors.groupingBy(MemberOverview::getCertificateId)) - .values() - .stream() - .map(group -> { - MemberOverview e = group.getFirst(); - return new MemberOverviewCertificateDto(e.getCertificateId(), - new MemberOverviewCertificateTypeDto(e - .getCertificateTypeName(), - e - .getCertificateTypeComment()), - e.getCertificateCompletedAt(), - e.getCertificateValidUntil(), - e.getCertificateComment()); - }) - .toList(); + if (row.getExperienceId() != null) { + experienceMap.putIfAbsent(row.getExperienceId(), mapToExperience(row)); + } - List leadershipExperiences = memberOverviews - .stream() - .filter(e -> e.getCertificateId() != null && CertificateKind.CERTIFICATE != e.getLeadershipTypeKind()) - .collect(Collectors.groupingBy(MemberOverview::getCertificateId)) - .values() - .stream() - .map(group -> { - MemberOverview e = group.getFirst(); - return new MemberOverviewLeadershipExperienceDto(e.getCertificateId(), - new MemberOverviewLeadershipExperienceTypeDto(e - .getCertificateTypeName(), - e - .getCertificateTypeComment(), - e - .getLeadershipTypeKind()), - e.getCertificateComment()); - }) - .toList(); + if (row.getCertificateId() != null) { + if (row.getLeadershipTypeKind().isLeadershipExperienceType()) { + leadershipMap.putIfAbsent(row.getCertificateId(), mapToLeadershipExperience(row)); + } else { + certificateMap.putIfAbsent(row.getCertificateId(), mapToCertificate(row)); + } + } + } - MemberCvDto cvDto = new MemberCvDto(degrees, experiences, certificates, leadershipExperiences); + MemberCvDto cvDto = new MemberCvDto(new ArrayList<>(degreeMap.values()), + new ArrayList<>(experienceMap.values()), + new ArrayList<>(certificateMap.values()), + new ArrayList<>(leadershipMap.values())); - return new MemberOverviewDto(memberDto, cvDto); + return new MemberOverviewDto(getMemberDto(memberOverviews), cvDto); } - public MemberOverviewMemberDto getMemberDto(List memberOverviews) { + private MemberOverviewMemberDto getMemberDto(List memberOverviews) { MemberOverview first = memberOverviews.getFirst(); - return new MemberOverviewMemberDto(first.getMemberId(), first.getFirstName(), first.getLastName(), @@ -117,4 +67,36 @@ public MemberOverviewMemberDto getMemberDto(List memberOverviews first.getBirthDate(), first.getOrganisationUnitName()); } -} + + private MemberOverviewDegreeDto mapToDegree(MemberOverview e) { + return new MemberOverviewDegreeDto(e.getDegreeId(), + e.getDegreeName(), + new MemberOverviewDegreeTypeDto(e.getDegreeTypeName()), + e.getDegreeStartDate(), + e.getDegreeEndDate()); + } + + private MemberOverviewExperienceDto mapToExperience(MemberOverview e) { + return new MemberOverviewExperienceDto(e.getExperienceId(), + e.getExperienceName(), + e.getExperienceEmployer(), + new MemberOverviewExperienceTypeDto(e.getExperienceTypeName()), + e.getExperienceComment(), + e.getExperienceStartDate(), + e.getExperienceEndDate()); + } + + private MemberOverviewCertificateDto mapToCertificate(MemberOverview e) { + return new MemberOverviewCertificateDto(e.getCertificateId(), + new MemberOverviewCertificateTypeDto(e.getCertificateTypeName()), + e.getCertificateCompletedAt(), + e.getCertificateComment()); + } + + private MemberOverviewLeadershipExperienceDto mapToLeadershipExperience(MemberOverview e) { + return new MemberOverviewLeadershipExperienceDto(e.getCertificateId(), + new MemberOverviewLeadershipExperienceTypeDto(e + .getCertificateTypeName(), e.getLeadershipTypeKind()), + e.getCertificateComment()); + } +} \ No newline at end of file diff --git a/backend/src/main/java/ch/puzzle/pcts/model/memberoverview/MemberOverview.java b/backend/src/main/java/ch/puzzle/pcts/model/memberoverview/MemberOverview.java index ca1ede9e2..58b2be642 100644 --- a/backend/src/main/java/ch/puzzle/pcts/model/memberoverview/MemberOverview.java +++ b/backend/src/main/java/ch/puzzle/pcts/model/memberoverview/MemberOverview.java @@ -29,11 +29,9 @@ public class MemberOverview implements Model { private Long certificateId; private LocalDate certificateCompletedAt; - private LocalDate certificateValidUntil; private String certificateComment; private String certificateTypeName; - private String certificateTypeComment; @Enumerated(EnumType.STRING) private CertificateKind leadershipTypeKind; @@ -41,7 +39,6 @@ public class MemberOverview implements Model { private String degreeName; private LocalDate degreeStartDate; private LocalDate degreeEndDate; - private String degreeComment; private String degreeTypeName; @@ -66,15 +63,12 @@ private MemberOverview(Builder builder) { organisationUnitName = trim(builder.organisationUnitName); certificateId = builder.certificateId; certificateCompletedAt = builder.certificateCompletedAt; - certificateValidUntil = builder.certificateValidUntil; certificateComment = trim(builder.certificateComment); certificateTypeName = trim(builder.certificateTypeName); - certificateTypeComment = trim(builder.certificateTypeComment); degreeId = builder.degreeId; degreeName = trim(builder.degreeName); degreeStartDate = builder.degreeStartDate; degreeEndDate = builder.degreeEndDate; - degreeComment = builder.degreeComment; leadershipTypeKind = builder.leadershipTypeKind; degreeTypeName = trim(builder.degreeTypeName); experienceId = builder.experienceId; @@ -180,14 +174,6 @@ public void setCertificateCompletedAt(LocalDate certificateCompletedAt) { this.certificateCompletedAt = certificateCompletedAt; } - public LocalDate getCertificateValidUntil() { - return certificateValidUntil; - } - - public void setCertificateValidUntil(LocalDate certificateValidUntil) { - this.certificateValidUntil = certificateValidUntil; - } - public String getCertificateComment() { return certificateComment; } @@ -204,14 +190,6 @@ public void setCertificateTypeName(String certificateTypeName) { this.certificateTypeName = trim(certificateTypeName); } - public String getCertificateTypeComment() { - return certificateTypeComment; - } - - public void setCertificateTypeComment(String certificateTypeComment) { - this.certificateTypeComment = trim(certificateTypeComment); - } - public Long getDegreeId() { return degreeId; } @@ -240,18 +218,10 @@ public LocalDate getDegreeEndDate() { return degreeEndDate; } - public String getDegreeComment() { - return degreeComment; - } - public void setDegreeEndDate(LocalDate degreeEndDate) { this.degreeEndDate = degreeEndDate; } - public void setDegreeComment(String degreeComment) { - this.degreeComment = trim(degreeComment); - } - public CertificateKind getLeadershipTypeKind() { return leadershipTypeKind; } @@ -353,17 +323,14 @@ public static final class Builder { private Long certificateId; private LocalDate certificateCompletedAt; - private LocalDate certificateValidUntil; private String certificateComment; private String certificateTypeName; - private String certificateTypeComment; private Long degreeId; private String degreeName; private LocalDate degreeStartDate; private LocalDate degreeEndDate; - private String degreeComment; private CertificateKind leadershipTypeKind; @@ -440,11 +407,6 @@ public Builder withCertificateCompletedAt(LocalDate certificateCompletedAt) { return this; } - public Builder withCertificateValidUntil(LocalDate certificateValidUntil) { - this.certificateValidUntil = certificateValidUntil; - return this; - } - public Builder withCertificateComment(String certificateComment) { this.certificateComment = trim(certificateComment); return this; @@ -455,11 +417,6 @@ public Builder withCertificateTypeName(String certificateTypeName) { return this; } - public Builder withCertificateTypeComment(String certificateTypeComment) { - this.certificateTypeComment = trim(certificateTypeComment); - return this; - } - public Builder withleadershipTypeKind(CertificateKind leadershipTypeKind) { this.leadershipTypeKind = leadershipTypeKind; return this; @@ -485,11 +442,6 @@ public Builder withDegreeEndDate(LocalDate degreeEndDate) { return this; } - public Builder withDegreeComment(String degreeComment) { - this.degreeComment = trim(degreeComment); - return this; - } - public Builder withDegreeTypeName(String degreeTypeName) { this.degreeTypeName = trim(degreeTypeName); return this; diff --git a/backend/src/main/java/ch/puzzle/pcts/service/business/MemberOverviewBusinessService.java b/backend/src/main/java/ch/puzzle/pcts/service/business/MemberOverviewBusinessService.java index fb3907f8e..dc843b59f 100644 --- a/backend/src/main/java/ch/puzzle/pcts/service/business/MemberOverviewBusinessService.java +++ b/backend/src/main/java/ch/puzzle/pcts/service/business/MemberOverviewBusinessService.java @@ -2,10 +2,16 @@ import static ch.puzzle.pcts.Constants.MEMBER_OVERVIEW; +import ch.puzzle.pcts.dto.error.ErrorKey; +import ch.puzzle.pcts.dto.error.FieldKey; +import ch.puzzle.pcts.dto.error.GenericErrorDto; +import ch.puzzle.pcts.exception.PCTSException; import ch.puzzle.pcts.model.memberoverview.MemberOverview; import ch.puzzle.pcts.service.persistence.MemberOverviewPersistenceService; import ch.puzzle.pcts.service.validation.MemberOverviewValidationService; import java.util.List; +import java.util.Map; +import org.springframework.http.HttpStatus; import org.springframework.stereotype.Service; @Service @@ -23,7 +29,23 @@ public MemberOverviewBusinessService(MemberOverviewPersistenceService persistenc public List getById(Long id) { validationService.validateOnGetById(id); - return persistenceService.getById(id); + + List result = persistenceService.getById(id); + + if (result.isEmpty()) { + throw new PCTSException(HttpStatus.NOT_FOUND, + List + .of(new GenericErrorDto(ErrorKey.NOT_FOUND, + Map + .of(FieldKey.ENTITY, + entityName(), + FieldKey.FIELD, + "id", + FieldKey.IS, + id.toString())))); + } + + return result; } protected String entityName() { From a9654ede9de2603e8e38a95ad5ae3e33cdd98a2a Mon Sep 17 00:00:00 2001 From: Manuel Date: Wed, 7 Jan 2026 13:50:26 +0100 Subject: [PATCH 16/30] test(backend): Fix corresponding tests and test data #50 --- .../MemberOverviewControllerIT.java | 43 ++++++++++++++++--- .../pcts/mapper/MemberOverviewMapperTest.java | 1 - .../java/ch/puzzle/pcts/util/TestData.java | 30 ++----------- 3 files changed, 39 insertions(+), 35 deletions(-) diff --git a/backend/src/test/java/ch/puzzle/pcts/controller/MemberOverviewControllerIT.java b/backend/src/test/java/ch/puzzle/pcts/controller/MemberOverviewControllerIT.java index 5ebf324e7..c77ecaaa3 100644 --- a/backend/src/test/java/ch/puzzle/pcts/controller/MemberOverviewControllerIT.java +++ b/backend/src/test/java/ch/puzzle/pcts/controller/MemberOverviewControllerIT.java @@ -3,13 +3,18 @@ import static ch.puzzle.pcts.util.TestData.MEMBER_1_OVERVIEWS; import static ch.puzzle.pcts.util.TestData.MEMBER_1_OVERVIEW_DTO; import static org.mockito.ArgumentMatchers.anyLong; -import static org.mockito.Mockito.times; -import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.*; +import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.csrf; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; import ch.puzzle.pcts.SpringSecurityConfig; +import ch.puzzle.pcts.dto.error.ErrorKey; +import ch.puzzle.pcts.dto.error.FieldKey; +import ch.puzzle.pcts.dto.error.GenericErrorDto; import ch.puzzle.pcts.dto.memberoverview.MemberOverviewDto; +import ch.puzzle.pcts.exception.PCTSException; import ch.puzzle.pcts.mapper.MemberOverviewMapper; import ch.puzzle.pcts.model.memberoverview.MemberOverview; import ch.puzzle.pcts.service.business.MemberOverviewBusinessService; @@ -18,16 +23,17 @@ import com.fasterxml.jackson.databind.SerializationFeature; import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; import java.util.List; +import java.util.Map; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.BDDMockito; import org.mockito.junit.jupiter.MockitoExtension; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest; +import org.springframework.boot.webmvc.test.autoconfigure.WebMvcTest; import org.springframework.context.annotation.Import; +import org.springframework.http.HttpStatus; import org.springframework.http.MediaType; -import org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors; import org.springframework.test.context.bean.override.mockito.MockitoBean; import org.springframework.test.web.servlet.MockMvc; @@ -63,13 +69,36 @@ void shouldGetMemberOverviewById() throws Exception { BDDMockito.given(mapper.toDto(memberOverviews)).willReturn(expectedDto); mvc - .perform(get(BASEURL + "/" + memberId) - .with(SecurityMockMvcRequestPostProcessors.csrf()) - .accept(MediaType.APPLICATION_JSON)) + .perform(get(BASEURL + "/" + memberId).with(csrf()).accept(MediaType.APPLICATION_JSON)) .andExpect(status().isOk()) .andExpect(JsonDtoMatcher.matchesDto(expectedDto, "$")); verify(service, times(1)).getById(memberId); verify(mapper, times(1)).toDto(memberOverviews); } + + @DisplayName("Should return 404 with NOT_FOUND error when member does not exist") + @Test + void shouldReturn404WhenMemberNotFound() throws Exception { + Long memberId = 99L; + + Map attributes = Map + .of(FieldKey.ENTITY, "Member", FieldKey.FIELD, "id", FieldKey.IS, memberId.toString()); + + GenericErrorDto error = new GenericErrorDto(ErrorKey.NOT_FOUND, attributes); + + BDDMockito.given(service.getById(memberId)).willThrow(new PCTSException(HttpStatus.NOT_FOUND, List.of(error))); + + mvc + .perform(get(BASEURL + "/" + memberId).with(csrf()).accept(MediaType.APPLICATION_JSON)) + .andExpect(status().isNotFound()) + .andExpect(jsonPath("$").isArray()) + .andExpect(jsonPath("$[0].key").value("NOT_FOUND")) + .andExpect(jsonPath("$[0].values.ENTITY").value("Member")) + .andExpect(jsonPath("$[0].values.FIELD").value("id")) + .andExpect(jsonPath("$[0].values.IS").value("99")); + + verify(service).getById(memberId); + verifyNoInteractions(mapper); + } } diff --git a/backend/src/test/java/ch/puzzle/pcts/mapper/MemberOverviewMapperTest.java b/backend/src/test/java/ch/puzzle/pcts/mapper/MemberOverviewMapperTest.java index 80617eb11..b21edcf51 100644 --- a/backend/src/test/java/ch/puzzle/pcts/mapper/MemberOverviewMapperTest.java +++ b/backend/src/test/java/ch/puzzle/pcts/mapper/MemberOverviewMapperTest.java @@ -51,7 +51,6 @@ void shouldReturnMemberOverviewDto() { assertThat(degree.type().name()).isEqualTo("Degree type 1"); assertThat(degree.startDate()).isEqualTo(LocalDate.of(2015, 9, 1)); assertThat(degree.endDate()).isEqualTo(LocalDate.of(2020, 6, 1)); - assertThat(degree.comment()).isEqualTo("Comment"); assertThat(cv.experiences()).hasSize(2); assertThat(cv.experiences()).extracting(MemberOverviewExperienceDto::id).containsExactlyInAnyOrder(1L, 2L); diff --git a/backend/src/test/java/ch/puzzle/pcts/util/TestData.java b/backend/src/test/java/ch/puzzle/pcts/util/TestData.java index f28fb7ece..521890794 100644 --- a/backend/src/test/java/ch/puzzle/pcts/util/TestData.java +++ b/backend/src/test/java/ch/puzzle/pcts/util/TestData.java @@ -266,16 +266,13 @@ private TestData() { .withOrganisationUnitName(MEMBER_1.getOrganisationUnit().getName()) .withCertificateId(CERTIFICATE_1.getId()) .withCertificateCompletedAt(CERTIFICATE_1.getCompletedAt()) - .withCertificateValidUntil(CERTIFICATE_1.getValidUntil()) .withCertificateComment(CERTIFICATE_1.getComment()) .withCertificateTypeName(CERTIFICATE_1.getCertificateType().getName()) - .withCertificateTypeComment(CERTIFICATE_1.getCertificateType().getComment()) .withleadershipTypeKind(CertificateKind.CERTIFICATE) .withDegreeId(DEGREE_1.getId()) .withDegreeName(DEGREE_1.getName()) .withDegreeStartDate(DEGREE_1.getStartDate()) .withDegreeEndDate(DEGREE_1.getEndDate()) - .withDegreeComment(DEGREE_1.getComment()) .withDegreeTypeName(DEGREE_1.getDegreeType().getName()) .withExperienceId(EXPERIENCE_1.getId()) .withExperienceName(EXPERIENCE_1.getName()) @@ -299,16 +296,13 @@ private TestData() { .withOrganisationUnitName(MEMBER_1.getOrganisationUnit().getName()) .withCertificateId(CERTIFICATE_1.getId()) .withCertificateCompletedAt(CERTIFICATE_1.getCompletedAt()) - .withCertificateValidUntil(CERTIFICATE_1.getValidUntil()) .withCertificateComment(CERTIFICATE_1.getComment()) .withCertificateTypeName(CERTIFICATE_1.getCertificateType().getName()) - .withCertificateTypeComment(CERTIFICATE_1.getCertificateType().getComment()) .withleadershipTypeKind(CertificateKind.CERTIFICATE) .withDegreeId(DEGREE_1.getId()) .withDegreeName(DEGREE_1.getName()) .withDegreeStartDate(DEGREE_1.getStartDate()) .withDegreeEndDate(DEGREE_1.getEndDate()) - .withDegreeComment(DEGREE_1.getComment()) .withDegreeTypeName(DEGREE_1.getDegreeType().getName()) .withExperienceId(EXPERIENCE_2.getId()) .withExperienceName(EXPERIENCE_2.getName()) @@ -332,16 +326,13 @@ private TestData() { .withOrganisationUnitName(MEMBER_1.getOrganisationUnit().getName()) .withCertificateId(CERTIFICATE_4.getId()) .withCertificateCompletedAt(CERTIFICATE_4.getCompletedAt()) - .withCertificateValidUntil(CERTIFICATE_4.getValidUntil()) .withCertificateComment(CERTIFICATE_4.getComment()) .withCertificateTypeName(CERTIFICATE_4.getCertificateType().getName()) - .withCertificateTypeComment(CERTIFICATE_4.getCertificateType().getComment()) .withleadershipTypeKind(CertificateKind.CERTIFICATE) .withDegreeId(DEGREE_1.getId()) .withDegreeName(DEGREE_1.getName()) .withDegreeStartDate(DEGREE_1.getStartDate()) .withDegreeEndDate(DEGREE_1.getEndDate()) - .withDegreeComment(DEGREE_1.getComment()) .withDegreeTypeName(DEGREE_1.getDegreeType().getName()) .withExperienceId(EXPERIENCE_1.getId()) .withExperienceName(EXPERIENCE_1.getName()) @@ -365,16 +356,13 @@ private TestData() { .withOrganisationUnitName(MEMBER_1.getOrganisationUnit().getName()) .withCertificateId(CERTIFICATE_4.getId()) .withCertificateCompletedAt(CERTIFICATE_4.getCompletedAt()) - .withCertificateValidUntil(CERTIFICATE_4.getValidUntil()) .withCertificateComment(CERTIFICATE_4.getComment()) .withCertificateTypeName(CERTIFICATE_4.getCertificateType().getName()) - .withCertificateTypeComment(CERTIFICATE_4.getCertificateType().getComment()) .withleadershipTypeKind(CertificateKind.CERTIFICATE) .withDegreeId(DEGREE_1.getId()) .withDegreeName(DEGREE_1.getName()) .withDegreeStartDate(DEGREE_1.getStartDate()) .withDegreeEndDate(DEGREE_1.getEndDate()) - .withDegreeComment(DEGREE_1.getComment()) .withDegreeTypeName(DEGREE_1.getDegreeType().getName()) .withExperienceId(EXPERIENCE_2.getId()) .withExperienceName(EXPERIENCE_2.getName()) @@ -413,9 +401,7 @@ private TestData() { DEGREE_1 .getStartDate(), DEGREE_1 - .getEndDate(), - DEGREE_1 - .getComment())), + .getEndDate())), List .of(new MemberOverviewExperienceDto(EXPERIENCE_1 .getId(), @@ -452,28 +438,18 @@ private TestData() { .getId(), new MemberOverviewCertificateTypeDto(CERTIFICATE_1 .getCertificateType() - .getName(), - CERTIFICATE_1 - .getCertificateType() - .getComment()), + .getName()), CERTIFICATE_1 .getCompletedAt(), - CERTIFICATE_1 - .getValidUntil(), CERTIFICATE_1 .getComment()), new MemberOverviewCertificateDto(CERTIFICATE_4 .getId(), new MemberOverviewCertificateTypeDto(CERTIFICATE_4 .getCertificateType() - .getName(), - CERTIFICATE_4 - .getCertificateType() - .getComment()), + .getName()), CERTIFICATE_4 .getCompletedAt(), - CERTIFICATE_4 - .getValidUntil(), CERTIFICATE_4 .getComment())), List.of())); From fe192304c2952554550deba55cc3a2b762bd9a34 Mon Sep 17 00:00:00 2001 From: Manuel Date: Wed, 7 Jan 2026 15:10:40 +0100 Subject: [PATCH 17/30] refactor(controller): Clarify naming #50 --- .../pcts/controller/MemberOverviewController.java | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/backend/src/main/java/ch/puzzle/pcts/controller/MemberOverviewController.java b/backend/src/main/java/ch/puzzle/pcts/controller/MemberOverviewController.java index 25639ecd8..b343db886 100644 --- a/backend/src/main/java/ch/puzzle/pcts/controller/MemberOverviewController.java +++ b/backend/src/main/java/ch/puzzle/pcts/controller/MemberOverviewController.java @@ -21,12 +21,12 @@ @RequestMapping("/api/v1/member-overviews") @Tag(name = "member-overviews", description = "Get the member and everything associated with the member") public class MemberOverviewController { - private final MemberOverviewMapper mapper; - private final MemberOverviewBusinessService service; + private final MemberOverviewMapper memberOverviewMapper; + private final MemberOverviewBusinessService memberOverviewBusinessService; public MemberOverviewController(MemberOverviewMapper mapper, MemberOverviewBusinessService service) { - this.mapper = mapper; - this.service = service; + this.memberOverviewMapper = mapper; + this.memberOverviewBusinessService = service; } @Operation(summary = "Get the member-overview by memberId") @@ -35,7 +35,7 @@ public MemberOverviewController(MemberOverviewMapper mapper, MemberOverviewBusin @GetMapping("{memberId}") public ResponseEntity getMemberOverviewByMemberId(@Parameter(description = "The ID of the member whose overview should be retrieved.", required = true) @PathVariable Long memberId) { - List memberOverviews = service.getById(memberId); - return ResponseEntity.ok(mapper.toDto(memberOverviews)); + List memberOverviews = memberOverviewBusinessService.getById(memberId); + return ResponseEntity.ok(memberOverviewMapper.toDto(memberOverviews)); } } From bdfb6723c3b764c1203884a3d019fccf4d753933 Mon Sep 17 00:00:00 2001 From: Manuel Date: Wed, 7 Jan 2026 15:13:43 +0100 Subject: [PATCH 18/30] test(mapper): Add additional tests #50 --- .../pcts/mapper/MemberOverviewMapperTest.java | 80 ++++----- .../java/ch/puzzle/pcts/util/TestData.java | 167 ++++++++++++++++++ 2 files changed, 203 insertions(+), 44 deletions(-) diff --git a/backend/src/test/java/ch/puzzle/pcts/mapper/MemberOverviewMapperTest.java b/backend/src/test/java/ch/puzzle/pcts/mapper/MemberOverviewMapperTest.java index b21edcf51..df5a507e0 100644 --- a/backend/src/test/java/ch/puzzle/pcts/mapper/MemberOverviewMapperTest.java +++ b/backend/src/test/java/ch/puzzle/pcts/mapper/MemberOverviewMapperTest.java @@ -1,19 +1,19 @@ package ch.puzzle.pcts.mapper; -import static ch.puzzle.pcts.util.TestData.MEMBER_1_OVERVIEWS; +import static ch.puzzle.pcts.util.TestData.*; import static org.assertj.core.api.Assertions.assertThat; -import ch.puzzle.pcts.dto.memberoverview.MemberCvDto; import ch.puzzle.pcts.dto.memberoverview.MemberOverviewDto; -import ch.puzzle.pcts.dto.memberoverview.MemberOverviewMemberDto; -import ch.puzzle.pcts.dto.memberoverview.certificate.MemberOverviewCertificateDto; -import ch.puzzle.pcts.dto.memberoverview.degree.MemberOverviewDegreeDto; -import ch.puzzle.pcts.dto.memberoverview.experience.MemberOverviewExperienceDto; -import ch.puzzle.pcts.model.member.EmploymentState; -import java.time.LocalDate; +import ch.puzzle.pcts.model.memberoverview.MemberOverview; +import java.util.Collections; +import java.util.List; +import java.util.stream.Stream; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; import org.mockito.InjectMocks; import org.mockito.junit.jupiter.MockitoExtension; @@ -23,41 +23,33 @@ class MemberOverviewMapperTest { @InjectMocks private MemberOverviewMapper mapper; - @DisplayName("Should map List to MemberOverviewDto") + @DisplayName("Should map valid MemberOverview lists to MemberOverviewDto") + @ParameterizedTest(name = "{index} => {0}") + @MethodSource("provideMemberOverviews") + void shouldReturnMemberOverviewDto(String description, List inputOverviews, + MemberOverviewDto expectedDto) { + MemberOverviewDto actualDto = mapper.toDto(inputOverviews); + + assertThat(actualDto).isNotNull(); + assertThat(actualDto).usingRecursiveComparison().ignoringCollectionOrder().isEqualTo(expectedDto); + } + + @Test + @DisplayName("Should return null when input list is null") + void shouldReturnNullWhenInputIsNull() { + assertThat(mapper.toDto(null)).isNull(); + } + @Test - void shouldReturnMemberOverviewDto() { - MemberOverviewDto dto = mapper.toDto(MEMBER_1_OVERVIEWS); - - assertThat(dto).isNotNull(); - assertThat(dto.member()).isNotNull(); - assertThat(dto.cv()).isNotNull(); - - MemberOverviewMemberDto member = dto.member(); - assertThat(member.id()).isEqualTo(1L); - assertThat(member.firstName()).isEqualTo("Member 1"); - assertThat(member.lastName()).isEqualTo("Test"); - assertThat(member.abbreviation()).isEqualTo("M1"); - assertThat(member.employmentState()).isEqualTo(EmploymentState.MEMBER); - assertThat(member.dateOfHire()).isEqualTo(LocalDate.of(2021, 7, 15)); - assertThat(member.birthDate()).isEqualTo(LocalDate.of(1999, 8, 10)); - assertThat(member.organisationUnitName()).isEqualTo("OrganisationUnit 1"); - - MemberCvDto cv = dto.cv(); - - assertThat(cv.degrees()).hasSize(1); - MemberOverviewDegreeDto degree = cv.degrees().getFirst(); - assertThat(degree.id()).isEqualTo(1L); - assertThat(degree.name()).isEqualTo("Degree 1"); - assertThat(degree.type().name()).isEqualTo("Degree type 1"); - assertThat(degree.startDate()).isEqualTo(LocalDate.of(2015, 9, 1)); - assertThat(degree.endDate()).isEqualTo(LocalDate.of(2020, 6, 1)); - - assertThat(cv.experiences()).hasSize(2); - assertThat(cv.experiences()).extracting(MemberOverviewExperienceDto::id).containsExactlyInAnyOrder(1L, 2L); - - assertThat(cv.certificates()).hasSize(2); - assertThat(cv.certificates()).extracting(MemberOverviewCertificateDto::id).containsExactlyInAnyOrder(1L, 4L); - - assertThat(cv.leadershipExperiences()).isEmpty(); + @DisplayName("Should return null when input list is empty") + void shouldReturnNullWhenInputIsEmpty() { + assertThat(mapper.toDto(Collections.emptyList())).isNull(); + } + + private static Stream provideMemberOverviews() { + return Stream + .of(Arguments.of("Standard Member (Full CV)", MEMBER_1_OVERVIEWS, MEMBER_1_OVERVIEW_DTO), + Arguments.of("Member with multiple same-type items", MEMBER_2_OVERVIEWS, MEMBER_2_OVERVIEW_DTO), + Arguments.of("Member with NO CV data (Sparse)", MEMBER_EMPTY_CV_OVERVIEWS, MEMBER_EMPTY_CV_DTO)); } -} +} \ No newline at end of file diff --git a/backend/src/test/java/ch/puzzle/pcts/util/TestData.java b/backend/src/test/java/ch/puzzle/pcts/util/TestData.java index 521890794..f27bc4bde 100644 --- a/backend/src/test/java/ch/puzzle/pcts/util/TestData.java +++ b/backend/src/test/java/ch/puzzle/pcts/util/TestData.java @@ -373,6 +373,85 @@ private TestData() { .withExperienceTypeName(EXPERIENCE_2.getType().getName()) .build()); + public static final List MEMBER_2_OVERVIEWS = List + .of(MemberOverview.Builder + .builder() + .withUniqueRowId(5L) + .withMemberId(MEMBER_2.getId()) + .withFirstName(MEMBER_2.getFirstName()) + .withLastName(MEMBER_2.getLastName()) + .withAbbreviation(MEMBER_2.getAbbreviation()) + .withEmploymentState(MEMBER_2.getEmploymentState()) + .withDateOfHire(MEMBER_2.getDateOfHire()) + .withBirthDate(MEMBER_2.getBirthDate()) + .withOrganisationUnitName(MEMBER_2.getOrganisationUnit().getName()) + .withCertificateId(CERTIFICATE_2.getId()) + .withCertificateCompletedAt(CERTIFICATE_2.getCompletedAt()) + .withCertificateComment(CERTIFICATE_2.getComment()) + .withCertificateTypeName(CERTIFICATE_2.getCertificateType().getName()) + .withleadershipTypeKind(CertificateKind.CERTIFICATE) + .withDegreeId(DEGREE_2.getId()) + .withDegreeName(DEGREE_2.getName()) + .withDegreeStartDate(DEGREE_2.getStartDate()) + .withDegreeEndDate(DEGREE_2.getEndDate()) + .withDegreeTypeName(DEGREE_2.getDegreeType().getName()) + .withExperienceId(EXPERIENCE_3.getId()) + .withExperienceName(EXPERIENCE_3.getName()) + .withExperienceEmployer(EXPERIENCE_3.getEmployer()) + .withExperienceStartDate(EXPERIENCE_3.getStartDate()) + .withExperienceEndDate(EXPERIENCE_3.getEndDate()) + .withExperienceComment(EXPERIENCE_3.getComment()) + .withExperienceTypeName(EXPERIENCE_3.getType().getName()) + .build(), + + MemberOverview.Builder + .builder() + .withUniqueRowId(6L) + .withMemberId(MEMBER_2.getId()) + .withFirstName(MEMBER_2.getFirstName()) + .withLastName(MEMBER_2.getLastName()) + .withAbbreviation(MEMBER_2.getAbbreviation()) + .withEmploymentState(MEMBER_2.getEmploymentState()) + .withDateOfHire(MEMBER_2.getDateOfHire()) + .withBirthDate(MEMBER_2.getBirthDate()) + .withOrganisationUnitName(MEMBER_2.getOrganisationUnit().getName()) + .withCertificateId(CERTIFICATE_3.getId()) + .withCertificateCompletedAt(CERTIFICATE_3.getCompletedAt()) + .withCertificateComment(CERTIFICATE_3.getComment()) + .withCertificateTypeName(CERTIFICATE_3.getCertificateType().getName()) + .withleadershipTypeKind(CertificateKind.CERTIFICATE) + .withDegreeId(DEGREE_2.getId()) + .withDegreeName(DEGREE_2.getName()) + .withDegreeStartDate(DEGREE_2.getStartDate()) + .withDegreeEndDate(DEGREE_2.getEndDate()) + .withDegreeTypeName(DEGREE_2.getDegreeType().getName()) + .withExperienceId(EXPERIENCE_3.getId()) + .withExperienceName(EXPERIENCE_3.getName()) + .withExperienceEmployer(EXPERIENCE_3.getEmployer()) + .withExperienceStartDate(EXPERIENCE_3.getStartDate()) + .withExperienceEndDate(EXPERIENCE_3.getEndDate()) + .withExperienceComment(EXPERIENCE_3.getComment()) + .withExperienceTypeName(EXPERIENCE_3.getType().getName()) + .build()); + + public static final List MEMBER_EMPTY_CV_OVERVIEWS = List + .of(MemberOverview.Builder + .builder() + .withUniqueRowId(99L) + .withMemberId(MEMBER_1.getId()) + .withFirstName(MEMBER_1.getFirstName()) + .withLastName(MEMBER_1.getLastName()) + .withEmploymentState(MEMBER_1.getEmploymentState()) + .withAbbreviation(MEMBER_1.getAbbreviation()) + .withDateOfHire(MEMBER_1.getDateOfHire()) + .withBirthDate(MEMBER_1.getBirthDate()) + .withOrganisationUnitName(MEMBER_1.getOrganisationUnit().getName()) + // All CV IDs are NULL + .withCertificateId(null) + .withDegreeId(null) + .withExperienceId(null) + .build()); + public static final MemberOverviewDto MEMBER_1_OVERVIEW_DTO = new MemberOverviewDto(new MemberOverviewMemberDto(MEMBER_1 .getId(), MEMBER_1 @@ -454,6 +533,94 @@ private TestData() { .getComment())), List.of())); + public static final MemberOverviewDto MEMBER_2_OVERVIEW_DTO = new MemberOverviewDto(new MemberOverviewMemberDto(MEMBER_2 + .getId(), + MEMBER_2 + .getFirstName(), + MEMBER_2 + .getLastName(), + MEMBER_2 + .getEmploymentState(), + MEMBER_2 + .getAbbreviation(), + MEMBER_2 + .getDateOfHire(), + MEMBER_2 + .getBirthDate(), + MEMBER_2 + .getOrganisationUnit() + .getName()), + new MemberCvDto(List + .of(new MemberOverviewDegreeDto(DEGREE_2 + .getId(), + DEGREE_2 + .getName(), + new MemberOverviewDegreeTypeDto(DEGREE_2 + .getDegreeType() + .getName()), + DEGREE_2 + .getStartDate(), + DEGREE_2 + .getEndDate())), + List + .of(new MemberOverviewExperienceDto(EXPERIENCE_3 + .getId(), + EXPERIENCE_3 + .getName(), + EXPERIENCE_3 + .getEmployer(), + new MemberOverviewExperienceTypeDto(EXPERIENCE_3 + .getType() + .getName()), + EXPERIENCE_3 + .getComment(), + EXPERIENCE_3 + .getStartDate(), + EXPERIENCE_3 + .getEndDate())), + List + .of(new MemberOverviewCertificateDto(CERTIFICATE_2 + .getId(), + new MemberOverviewCertificateTypeDto(CERTIFICATE_2 + .getCertificateType() + .getName()), + CERTIFICATE_2 + .getCompletedAt(), + CERTIFICATE_2 + .getComment()), + new MemberOverviewCertificateDto(CERTIFICATE_3 + .getId(), + new MemberOverviewCertificateTypeDto(CERTIFICATE_3 + .getCertificateType() + .getName()), + CERTIFICATE_3 + .getCompletedAt(), + CERTIFICATE_3 + .getComment())), + List.of())); + + public static final MemberOverviewDto MEMBER_EMPTY_CV_DTO = new MemberOverviewDto(new MemberOverviewMemberDto(MEMBER_1 + .getId(), + MEMBER_1 + .getFirstName(), + MEMBER_1 + .getLastName(), + MEMBER_1 + .getEmploymentState(), + MEMBER_1 + .getAbbreviation(), + MEMBER_1 + .getDateOfHire(), + MEMBER_1 + .getBirthDate(), + MEMBER_1 + .getOrganisationUnit() + .getName()), + new MemberCvDto(List.of(), + List.of(), + List.of(), + List.of())); + public static final Calculation CALCULATION_1 = new Calculation(1L, MEMBER_1, ROLE_2, From 16e02969212f41a9d0b5074eceaa0b4149e02c5c Mon Sep 17 00:00:00 2001 From: Manuel Date: Fri, 9 Jan 2026 08:38:11 +0100 Subject: [PATCH 19/30] refactor(db): Use java composite primary key instead of rowid #50 --- .../model/memberoverview/MemberOverview.java | 34 ++++----- .../memberoverview/MemberOverviewId.java | 75 +++++++++++++++++++ .../db/migration/V0_0_14__add_member_view.sql | 73 ++++++++---------- .../pcts/architecture/ArchitectureTest.java | 2 + .../java/ch/puzzle/pcts/util/TestData.java | 7 -- 5 files changed, 124 insertions(+), 67 deletions(-) create mode 100644 backend/src/main/java/ch/puzzle/pcts/model/memberoverview/MemberOverviewId.java diff --git a/backend/src/main/java/ch/puzzle/pcts/model/memberoverview/MemberOverview.java b/backend/src/main/java/ch/puzzle/pcts/model/memberoverview/MemberOverview.java index 58b2be642..97bbf98dd 100644 --- a/backend/src/main/java/ch/puzzle/pcts/model/memberoverview/MemberOverview.java +++ b/backend/src/main/java/ch/puzzle/pcts/model/memberoverview/MemberOverview.java @@ -10,12 +10,10 @@ @Entity @Table(name = "member_overview") +@IdClass(MemberOverviewId.class) public class MemberOverview implements Model { @Id - @Column(name = "unique_row_id") - private Long uniqueRowId; - private Long memberId; private String firstName; private String lastName; @@ -27,6 +25,7 @@ public class MemberOverview implements Model { private EmploymentState employmentState; private String organisationUnitName; + @Id private Long certificateId; private LocalDate certificateCompletedAt; private String certificateComment; @@ -35,6 +34,7 @@ public class MemberOverview implements Model { @Enumerated(EnumType.STRING) private CertificateKind leadershipTypeKind; + @Id private Long degreeId; private String degreeName; private LocalDate degreeStartDate; @@ -42,6 +42,7 @@ public class MemberOverview implements Model { private String degreeTypeName; + @Id private Long experienceId; private String experienceName; private String experienceEmployer; @@ -52,7 +53,6 @@ public class MemberOverview implements Model { private String experienceTypeName; private MemberOverview(Builder builder) { - uniqueRowId = builder.uniqueRowId; memberId = builder.memberId; firstName = trim(builder.firstName); lastName = trim(builder.lastName); @@ -84,16 +84,6 @@ public MemberOverview() { } - @Override - public Long getId() { - return uniqueRowId; - } - - @Override - public void setId(Long uniqueRowId) { - this.uniqueRowId = uniqueRowId; - } - public Long getMemberId() { return memberId; } @@ -309,8 +299,17 @@ public String toString() { return super.toString(); } + @Override + public Long getId() { + return 0L; + } + + @Override + public void setId(Long id) { + + } + public static final class Builder { - private Long uniqueRowId; private Long memberId; private String firstName; private String lastName; @@ -352,11 +351,6 @@ public static Builder builder() { return new Builder(); } - public Builder withUniqueRowId(Long uniqueRowId) { - this.uniqueRowId = uniqueRowId; - return this; - } - public Builder withMemberId(Long memberId) { this.memberId = memberId; return this; diff --git a/backend/src/main/java/ch/puzzle/pcts/model/memberoverview/MemberOverviewId.java b/backend/src/main/java/ch/puzzle/pcts/model/memberoverview/MemberOverviewId.java new file mode 100644 index 000000000..1c1270f1b --- /dev/null +++ b/backend/src/main/java/ch/puzzle/pcts/model/memberoverview/MemberOverviewId.java @@ -0,0 +1,75 @@ +package ch.puzzle.pcts.model.memberoverview; + +import java.io.Serializable; +import java.util.Objects; + +public class MemberOverviewId implements Serializable { + + private Long memberId; + private Long certificateId; + private Long degreeId; + private Long experienceId; + + public MemberOverviewId() { + } + + public MemberOverviewId(Long memberId, Long certificateId, Long degreeId, Long experienceId) { + this.memberId = memberId; + this.certificateId = certificateId; + this.degreeId = degreeId; + this.experienceId = experienceId; + } + + public Long getMemberId() { + return memberId; + } + + public void setMemberId(Long memberId) { + this.memberId = memberId; + } + + public Long getCertificateId() { + return certificateId; + } + + public void setCertificateId(Long certificateId) { + this.certificateId = certificateId; + } + + public Long getDegreeId() { + return degreeId; + } + + public void setDegreeId(Long degreeId) { + this.degreeId = degreeId; + } + + public Long getExperienceId() { + return experienceId; + } + + public void setExperienceId(Long experienceId) { + this.experienceId = experienceId; + } + + @Override + public boolean equals(Object o) { + if (!(o instanceof MemberOverviewId that)) + return false; + return Objects.equals(getMemberId(), that.getMemberId()) + && Objects.equals(getCertificateId(), that.getCertificateId()) + && Objects.equals(getDegreeId(), that.getDegreeId()) + && Objects.equals(getExperienceId(), that.getExperienceId()); + } + + @Override + public int hashCode() { + return Objects.hash(getMemberId(), getCertificateId(), getDegreeId(), getExperienceId()); + } + + @Override + public String toString() { + return "MemberOverviewId{" + "memberId=" + memberId + ", certificateId=" + certificateId + ", degreeId=" + + degreeId + ", experienceId=" + experienceId + '}'; + } +} diff --git a/backend/src/main/resources/db/migration/V0_0_14__add_member_view.sql b/backend/src/main/resources/db/migration/V0_0_14__add_member_view.sql index d66557cc0..68efa641c 100644 --- a/backend/src/main/resources/db/migration/V0_0_14__add_member_view.sql +++ b/backend/src/main/resources/db/migration/V0_0_14__add_member_view.sql @@ -2,47 +2,40 @@ DROP VIEW IF EXISTS member_overview; CREATE VIEW member_overview AS SELECT - ROW_NUMBER() OVER ( - ORDER BY - m.id, - c.id, - d.id, - e.id - ) AS unique_row_id, - m.id AS member_id, - m.first_name, - m.last_name, - m.abbreviation, - m.employment_state, - m.date_of_hire, - m.birth_date, - - ou.name AS organisation_unit_name, - - c.id AS certificate_id, - c.completed_at AS certificate_completed_at, - c.valid_until AS certificate_valid_until, - c.comment AS certificate_comment, - - ct.name AS certificate_type_name, - ct.comment AS certificate_type_comment, - ct.certificate_kind AS leadership_type_kind, - - d.id AS degree_id, - d.name AS degree_name, - d.start_date AS degree_start_date, - d.end_date AS degree_end_date, - d.comment AS degree_comment, - dt.name AS degree_type_name, - - e.id AS experience_id, - e.name AS experience_name, - e.employer AS experience_employer, - e.start_date AS experience_start_date, - e.end_date AS experience_end_date, - e.comment AS experience_comment, - et.name AS experience_type_name + m.id AS member_id, + m.first_name, + m.last_name, + m.abbreviation, + m.employment_state, + m.date_of_hire, + m.birth_date, + + ou.name AS organisation_unit_name, + + COALESCE(c.id, 0) AS certificate_id, + c.completed_at AS certificate_completed_at, + c.valid_until AS certificate_valid_until, + c.comment AS certificate_comment, + + ct.name AS certificate_type_name, + ct.comment AS certificate_type_comment, + ct.certificate_kind AS leadership_type_kind, + + COALESCE(d.id, 0) AS degree_id, + d.name AS degree_name, + d.start_date AS degree_start_date, + d.end_date AS degree_end_date, + d.comment AS degree_comment, + dt.name AS degree_type_name, + + COALESCE(e.id, 0) AS experience_id, + e.name AS experience_name, + e.employer AS experience_employer, + e.start_date AS experience_start_date, + e.end_date AS experience_end_date, + e.comment AS experience_comment, + et.name AS experience_type_name FROM member m LEFT JOIN organisation_unit ou diff --git a/backend/src/test/java/ch/puzzle/pcts/architecture/ArchitectureTest.java b/backend/src/test/java/ch/puzzle/pcts/architecture/ArchitectureTest.java index d4f52f03b..f6b2eb34c 100644 --- a/backend/src/test/java/ch/puzzle/pcts/architecture/ArchitectureTest.java +++ b/backend/src/test/java/ch/puzzle/pcts/architecture/ArchitectureTest.java @@ -281,6 +281,8 @@ void modelShouldImplementInterfaceAndOverrideMethods() { .areNotEnums() .and() .areNotNestedClasses() + .and() + .haveSimpleNameNotEndingWith("Id") .should() .beAnnotatedWith(Entity.class) .andShould() diff --git a/backend/src/test/java/ch/puzzle/pcts/util/TestData.java b/backend/src/test/java/ch/puzzle/pcts/util/TestData.java index f27bc4bde..3eff296e0 100644 --- a/backend/src/test/java/ch/puzzle/pcts/util/TestData.java +++ b/backend/src/test/java/ch/puzzle/pcts/util/TestData.java @@ -255,7 +255,6 @@ private TestData() { public static final List MEMBER_1_OVERVIEWS = List .of(MemberOverview.Builder .builder() - .withUniqueRowId(1L) .withMemberId(MEMBER_1.getId()) .withFirstName(MEMBER_1.getFirstName()) .withLastName(MEMBER_1.getLastName()) @@ -285,7 +284,6 @@ private TestData() { MemberOverview.Builder .builder() - .withUniqueRowId(2L) .withMemberId(MEMBER_1.getId()) .withFirstName(MEMBER_1.getFirstName()) .withLastName(MEMBER_1.getLastName()) @@ -315,7 +313,6 @@ private TestData() { MemberOverview.Builder .builder() - .withUniqueRowId(3L) .withMemberId(MEMBER_1.getId()) .withFirstName(MEMBER_1.getFirstName()) .withLastName(MEMBER_1.getLastName()) @@ -345,7 +342,6 @@ private TestData() { MemberOverview.Builder .builder() - .withUniqueRowId(4L) .withMemberId(MEMBER_1.getId()) .withFirstName(MEMBER_1.getFirstName()) .withLastName(MEMBER_1.getLastName()) @@ -376,7 +372,6 @@ private TestData() { public static final List MEMBER_2_OVERVIEWS = List .of(MemberOverview.Builder .builder() - .withUniqueRowId(5L) .withMemberId(MEMBER_2.getId()) .withFirstName(MEMBER_2.getFirstName()) .withLastName(MEMBER_2.getLastName()) @@ -406,7 +401,6 @@ private TestData() { MemberOverview.Builder .builder() - .withUniqueRowId(6L) .withMemberId(MEMBER_2.getId()) .withFirstName(MEMBER_2.getFirstName()) .withLastName(MEMBER_2.getLastName()) @@ -437,7 +431,6 @@ private TestData() { public static final List MEMBER_EMPTY_CV_OVERVIEWS = List .of(MemberOverview.Builder .builder() - .withUniqueRowId(99L) .withMemberId(MEMBER_1.getId()) .withFirstName(MEMBER_1.getFirstName()) .withLastName(MEMBER_1.getLastName()) From e4ad548230a994b25585d6f9b3d2b96251d3d23d Mon Sep 17 00:00:00 2001 From: Manuel Date: Fri, 9 Jan 2026 09:13:53 +0100 Subject: [PATCH 20/30] refactor(dto): Fix name of dto to have correct prefix #50 --- .../{MemberCvDto.java => MemberOverviewCvDto.java} | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) rename backend/src/main/java/ch/puzzle/pcts/dto/memberoverview/{MemberCvDto.java => MemberOverviewCvDto.java} (81%) diff --git a/backend/src/main/java/ch/puzzle/pcts/dto/memberoverview/MemberCvDto.java b/backend/src/main/java/ch/puzzle/pcts/dto/memberoverview/MemberOverviewCvDto.java similarity index 81% rename from backend/src/main/java/ch/puzzle/pcts/dto/memberoverview/MemberCvDto.java rename to backend/src/main/java/ch/puzzle/pcts/dto/memberoverview/MemberOverviewCvDto.java index ed66d5f5b..3540cd31c 100644 --- a/backend/src/main/java/ch/puzzle/pcts/dto/memberoverview/MemberCvDto.java +++ b/backend/src/main/java/ch/puzzle/pcts/dto/memberoverview/MemberOverviewCvDto.java @@ -6,7 +6,7 @@ import ch.puzzle.pcts.dto.memberoverview.leadershipexperience.MemberOverviewLeadershipExperienceDto; import java.util.List; -public record MemberCvDto(List degrees, List experiences, +public record MemberOverviewCvDto(List degrees, List experiences, List certificates, List leadershipExperiences) { } From 89134d4e11b2f500ae895e01b79298db57bc129c Mon Sep 17 00:00:00 2001 From: Manuel Date: Fri, 9 Jan 2026 09:14:56 +0100 Subject: [PATCH 21/30] refactor(dto): Re-name other occurences of dto #50 --- .../dto/memberoverview/MemberOverviewDto.java | 2 +- .../pcts/mapper/MemberOverviewMapper.java | 8 +- .../java/ch/puzzle/pcts/util/TestData.java | 191 +++++++++--------- 3 files changed, 102 insertions(+), 99 deletions(-) diff --git a/backend/src/main/java/ch/puzzle/pcts/dto/memberoverview/MemberOverviewDto.java b/backend/src/main/java/ch/puzzle/pcts/dto/memberoverview/MemberOverviewDto.java index 3929d35c7..603793a75 100644 --- a/backend/src/main/java/ch/puzzle/pcts/dto/memberoverview/MemberOverviewDto.java +++ b/backend/src/main/java/ch/puzzle/pcts/dto/memberoverview/MemberOverviewDto.java @@ -1,4 +1,4 @@ package ch.puzzle.pcts.dto.memberoverview; -public record MemberOverviewDto(MemberOverviewMemberDto member, MemberCvDto cv) { +public record MemberOverviewDto(MemberOverviewMemberDto member, MemberOverviewCvDto cv) { } diff --git a/backend/src/main/java/ch/puzzle/pcts/mapper/MemberOverviewMapper.java b/backend/src/main/java/ch/puzzle/pcts/mapper/MemberOverviewMapper.java index 1c3fe875f..1b1b2ad0e 100644 --- a/backend/src/main/java/ch/puzzle/pcts/mapper/MemberOverviewMapper.java +++ b/backend/src/main/java/ch/puzzle/pcts/mapper/MemberOverviewMapper.java @@ -48,10 +48,10 @@ public MemberOverviewDto toDto(List memberOverviews) { } } - MemberCvDto cvDto = new MemberCvDto(new ArrayList<>(degreeMap.values()), - new ArrayList<>(experienceMap.values()), - new ArrayList<>(certificateMap.values()), - new ArrayList<>(leadershipMap.values())); + MemberOverviewCvDto cvDto = new MemberOverviewCvDto(new ArrayList<>(degreeMap.values()), + new ArrayList<>(experienceMap.values()), + new ArrayList<>(certificateMap.values()), + new ArrayList<>(leadershipMap.values())); return new MemberOverviewDto(getMemberDto(memberOverviews), cvDto); } diff --git a/backend/src/test/java/ch/puzzle/pcts/util/TestData.java b/backend/src/test/java/ch/puzzle/pcts/util/TestData.java index 3eff296e0..0a5699591 100644 --- a/backend/src/test/java/ch/puzzle/pcts/util/TestData.java +++ b/backend/src/test/java/ch/puzzle/pcts/util/TestData.java @@ -1,6 +1,6 @@ package ch.puzzle.pcts.util; -import ch.puzzle.pcts.dto.memberoverview.MemberCvDto; +import ch.puzzle.pcts.dto.memberoverview.MemberOverviewCvDto; import ch.puzzle.pcts.dto.memberoverview.MemberOverviewDto; import ch.puzzle.pcts.dto.memberoverview.MemberOverviewMemberDto; import ch.puzzle.pcts.dto.memberoverview.certificate.MemberOverviewCertificateDto; @@ -462,7 +462,7 @@ private TestData() { MEMBER_1 .getOrganisationUnit() .getName()), - new MemberCvDto(List + new MemberOverviewCvDto(List .of(new MemberOverviewDegreeDto(DEGREE_1 .getId(), DEGREE_1 @@ -474,57 +474,58 @@ private TestData() { .getStartDate(), DEGREE_1 .getEndDate())), - List - .of(new MemberOverviewExperienceDto(EXPERIENCE_1 - .getId(), - EXPERIENCE_1 - .getName(), - EXPERIENCE_1 - .getEmployer(), - new MemberOverviewExperienceTypeDto(EXPERIENCE_1 - .getType() - .getName()), - EXPERIENCE_1 - .getComment(), - EXPERIENCE_1 - .getStartDate(), - EXPERIENCE_1 - .getEndDate()), - new MemberOverviewExperienceDto(EXPERIENCE_2 - .getId(), - EXPERIENCE_2 - .getName(), - EXPERIENCE_2 - .getEmployer(), - new MemberOverviewExperienceTypeDto(EXPERIENCE_2 - .getType() - .getName()), - EXPERIENCE_2 - .getComment(), - EXPERIENCE_2 - .getStartDate(), - EXPERIENCE_2 - .getEndDate())), - List - .of(new MemberOverviewCertificateDto(CERTIFICATE_1 - .getId(), - new MemberOverviewCertificateTypeDto(CERTIFICATE_1 - .getCertificateType() - .getName()), - CERTIFICATE_1 - .getCompletedAt(), - CERTIFICATE_1 - .getComment()), - new MemberOverviewCertificateDto(CERTIFICATE_4 - .getId(), - new MemberOverviewCertificateTypeDto(CERTIFICATE_4 - .getCertificateType() - .getName()), - CERTIFICATE_4 - .getCompletedAt(), - CERTIFICATE_4 - .getComment())), - List.of())); + List + .of(new MemberOverviewExperienceDto(EXPERIENCE_1 + .getId(), + EXPERIENCE_1 + .getName(), + EXPERIENCE_1 + .getEmployer(), + new MemberOverviewExperienceTypeDto(EXPERIENCE_1 + .getType() + .getName()), + EXPERIENCE_1 + .getComment(), + EXPERIENCE_1 + .getStartDate(), + EXPERIENCE_1 + .getEndDate()), + new MemberOverviewExperienceDto(EXPERIENCE_2 + .getId(), + EXPERIENCE_2 + .getName(), + EXPERIENCE_2 + .getEmployer(), + new MemberOverviewExperienceTypeDto(EXPERIENCE_2 + .getType() + .getName()), + EXPERIENCE_2 + .getComment(), + EXPERIENCE_2 + .getStartDate(), + EXPERIENCE_2 + .getEndDate())), + List + .of(new MemberOverviewCertificateDto(CERTIFICATE_1 + .getId(), + new MemberOverviewCertificateTypeDto(CERTIFICATE_1 + .getCertificateType() + .getName()), + CERTIFICATE_1 + .getCompletedAt(), + CERTIFICATE_1 + .getComment()), + new MemberOverviewCertificateDto(CERTIFICATE_4 + .getId(), + new MemberOverviewCertificateTypeDto(CERTIFICATE_4 + .getCertificateType() + .getName()), + CERTIFICATE_4 + .getCompletedAt(), + CERTIFICATE_4 + .getComment())), + List + .of())); public static final MemberOverviewDto MEMBER_2_OVERVIEW_DTO = new MemberOverviewDto(new MemberOverviewMemberDto(MEMBER_2 .getId(), @@ -543,7 +544,7 @@ private TestData() { MEMBER_2 .getOrganisationUnit() .getName()), - new MemberCvDto(List + new MemberOverviewCvDto(List .of(new MemberOverviewDegreeDto(DEGREE_2 .getId(), DEGREE_2 @@ -555,42 +556,43 @@ private TestData() { .getStartDate(), DEGREE_2 .getEndDate())), - List - .of(new MemberOverviewExperienceDto(EXPERIENCE_3 - .getId(), - EXPERIENCE_3 - .getName(), - EXPERIENCE_3 - .getEmployer(), - new MemberOverviewExperienceTypeDto(EXPERIENCE_3 - .getType() - .getName()), - EXPERIENCE_3 - .getComment(), - EXPERIENCE_3 - .getStartDate(), - EXPERIENCE_3 - .getEndDate())), - List - .of(new MemberOverviewCertificateDto(CERTIFICATE_2 - .getId(), - new MemberOverviewCertificateTypeDto(CERTIFICATE_2 - .getCertificateType() - .getName()), - CERTIFICATE_2 - .getCompletedAt(), - CERTIFICATE_2 - .getComment()), - new MemberOverviewCertificateDto(CERTIFICATE_3 - .getId(), - new MemberOverviewCertificateTypeDto(CERTIFICATE_3 - .getCertificateType() - .getName()), - CERTIFICATE_3 - .getCompletedAt(), - CERTIFICATE_3 - .getComment())), - List.of())); + List + .of(new MemberOverviewExperienceDto(EXPERIENCE_3 + .getId(), + EXPERIENCE_3 + .getName(), + EXPERIENCE_3 + .getEmployer(), + new MemberOverviewExperienceTypeDto(EXPERIENCE_3 + .getType() + .getName()), + EXPERIENCE_3 + .getComment(), + EXPERIENCE_3 + .getStartDate(), + EXPERIENCE_3 + .getEndDate())), + List + .of(new MemberOverviewCertificateDto(CERTIFICATE_2 + .getId(), + new MemberOverviewCertificateTypeDto(CERTIFICATE_2 + .getCertificateType() + .getName()), + CERTIFICATE_2 + .getCompletedAt(), + CERTIFICATE_2 + .getComment()), + new MemberOverviewCertificateDto(CERTIFICATE_3 + .getId(), + new MemberOverviewCertificateTypeDto(CERTIFICATE_3 + .getCertificateType() + .getName()), + CERTIFICATE_3 + .getCompletedAt(), + CERTIFICATE_3 + .getComment())), + List + .of())); public static final MemberOverviewDto MEMBER_EMPTY_CV_DTO = new MemberOverviewDto(new MemberOverviewMemberDto(MEMBER_1 .getId(), @@ -609,10 +611,11 @@ private TestData() { MEMBER_1 .getOrganisationUnit() .getName()), - new MemberCvDto(List.of(), - List.of(), - List.of(), - List.of())); + new MemberOverviewCvDto(List.of(), + List.of(), + List.of(), + List + .of())); public static final Calculation CALCULATION_1 = new Calculation(1L, MEMBER_1, From 1525437c44a5ca4e7a7c700e7cbe1204908ba17c Mon Sep 17 00:00:00 2001 From: Manuel Date: Fri, 9 Jan 2026 10:24:56 +0100 Subject: [PATCH 22/30] fix(model): Remove inheritation of model #50 --- .../model/memberoverview/MemberOverview.java | 85 +++++++++++++++---- .../MemberOverviewValidationService.java | 17 +++- .../pcts/architecture/ArchitectureTest.java | 3 + 3 files changed, 86 insertions(+), 19 deletions(-) diff --git a/backend/src/main/java/ch/puzzle/pcts/model/memberoverview/MemberOverview.java b/backend/src/main/java/ch/puzzle/pcts/model/memberoverview/MemberOverview.java index 97bbf98dd..acfe6a924 100644 --- a/backend/src/main/java/ch/puzzle/pcts/model/memberoverview/MemberOverview.java +++ b/backend/src/main/java/ch/puzzle/pcts/model/memberoverview/MemberOverview.java @@ -2,16 +2,16 @@ import static org.apache.commons.lang3.StringUtils.trim; -import ch.puzzle.pcts.model.Model; import ch.puzzle.pcts.model.certificatetype.CertificateKind; import ch.puzzle.pcts.model.member.EmploymentState; import jakarta.persistence.*; import java.time.LocalDate; +import java.util.Objects; @Entity @Table(name = "member_overview") @IdClass(MemberOverviewId.class) -public class MemberOverview implements Model { +public class MemberOverview { @Id private Long memberId; @@ -285,28 +285,79 @@ public void setExperienceTypeName(String experienceTypeName) { } @Override - public int hashCode() { - return super.hashCode(); + public boolean equals(Object o) { + if (!(o instanceof MemberOverview that)) + return false; + return Objects.equals(getMemberId(), that.getMemberId()) && Objects.equals(getFirstName(), that.getFirstName()) + && Objects.equals(getLastName(), that.getLastName()) + && Objects.equals(getAbbreviation(), that.getAbbreviation()) + && Objects.equals(getBirthDate(), that.getBirthDate()) + && Objects.equals(getDateOfHire(), that.getDateOfHire()) + && getEmploymentState() == that.getEmploymentState() + && Objects.equals(getOrganisationUnitName(), that.getOrganisationUnitName()) + && Objects.equals(getCertificateId(), that.getCertificateId()) + && Objects.equals(getCertificateCompletedAt(), that.getCertificateCompletedAt()) + && Objects.equals(getCertificateComment(), that.getCertificateComment()) + && Objects.equals(getCertificateTypeName(), that.getCertificateTypeName()) + && getLeadershipTypeKind() == that.getLeadershipTypeKind() + && Objects.equals(getDegreeId(), that.getDegreeId()) + && Objects.equals(getDegreeName(), that.getDegreeName()) + && Objects.equals(getDegreeStartDate(), that.getDegreeStartDate()) + && Objects.equals(getDegreeEndDate(), that.getDegreeEndDate()) + && Objects.equals(getDegreeTypeName(), that.getDegreeTypeName()) + && Objects.equals(getExperienceId(), that.getExperienceId()) + && Objects.equals(getExperienceName(), that.getExperienceName()) + && Objects.equals(getExperienceEmployer(), that.getExperienceEmployer()) + && Objects.equals(getExperienceStartDate(), that.getExperienceStartDate()) + && Objects.equals(getExperienceEndDate(), that.getExperienceEndDate()) + && Objects.equals(getExperienceComment(), that.getExperienceComment()) + && Objects.equals(getExperienceTypeName(), that.getExperienceTypeName()); } @Override - public boolean equals(Object obj) { - return super.equals(obj); + public int hashCode() { + return Objects + .hash(getMemberId(), + getFirstName(), + getLastName(), + getAbbreviation(), + getBirthDate(), + getDateOfHire(), + getEmploymentState(), + getOrganisationUnitName(), + getCertificateId(), + getCertificateCompletedAt(), + getCertificateComment(), + getCertificateTypeName(), + getLeadershipTypeKind(), + getDegreeId(), + getDegreeName(), + getDegreeStartDate(), + getDegreeEndDate(), + getDegreeTypeName(), + getExperienceId(), + getExperienceName(), + getExperienceEmployer(), + getExperienceStartDate(), + getExperienceEndDate(), + getExperienceComment(), + getExperienceTypeName()); } @Override public String toString() { - return super.toString(); - } - - @Override - public Long getId() { - return 0L; - } - - @Override - public void setId(Long id) { - + return "MemberOverview{" + "memberId=" + memberId + ", firstName='" + firstName + '\'' + ", lastName='" + + lastName + '\'' + ", abbreviation='" + abbreviation + '\'' + ", birthDate=" + birthDate + + ", dateOfHire=" + dateOfHire + ", employmentState=" + employmentState + ", organisationUnitName='" + + organisationUnitName + '\'' + ", certificateId=" + certificateId + ", certificateCompletedAt=" + + certificateCompletedAt + ", certificateComment='" + certificateComment + '\'' + + ", certificateTypeName='" + certificateTypeName + '\'' + ", leadershipTypeKind=" + leadershipTypeKind + + ", degreeId=" + degreeId + ", degreeName='" + degreeName + '\'' + ", degreeStartDate=" + + degreeStartDate + ", degreeEndDate=" + degreeEndDate + ", degreeTypeName='" + degreeTypeName + '\'' + + ", experienceId=" + experienceId + ", experienceName='" + experienceName + '\'' + + ", experienceEmployer='" + experienceEmployer + '\'' + ", experienceStartDate=" + experienceStartDate + + ", experienceEndDate=" + experienceEndDate + ", experienceComment='" + experienceComment + '\'' + + ", experienceTypeName='" + experienceTypeName + '\'' + '}'; } public static final class Builder { diff --git a/backend/src/main/java/ch/puzzle/pcts/service/validation/MemberOverviewValidationService.java b/backend/src/main/java/ch/puzzle/pcts/service/validation/MemberOverviewValidationService.java index cc3fa7aa2..93d59db55 100644 --- a/backend/src/main/java/ch/puzzle/pcts/service/validation/MemberOverviewValidationService.java +++ b/backend/src/main/java/ch/puzzle/pcts/service/validation/MemberOverviewValidationService.java @@ -1,8 +1,21 @@ package ch.puzzle.pcts.service.validation; -import ch.puzzle.pcts.model.memberoverview.MemberOverview; +import static ch.puzzle.pcts.service.validation.ValidationBase.buildGenericErrorDto; + +import ch.puzzle.pcts.dto.error.ErrorKey; +import ch.puzzle.pcts.dto.error.FieldKey; +import ch.puzzle.pcts.exception.PCTSException; +import java.util.Map; +import org.springframework.http.HttpStatus; import org.springframework.stereotype.Service; @Service -public class MemberOverviewValidationService extends ValidationBase { +public class MemberOverviewValidationService { + + public void validateOnGetById(Long id) { + if (id == null) { + throw new PCTSException(HttpStatus.BAD_REQUEST, + buildGenericErrorDto(ErrorKey.VALIDATION, Map.of(FieldKey.FIELD, "id"))); + } + } } diff --git a/backend/src/test/java/ch/puzzle/pcts/architecture/ArchitectureTest.java b/backend/src/test/java/ch/puzzle/pcts/architecture/ArchitectureTest.java index f6b2eb34c..2b41d99ec 100644 --- a/backend/src/test/java/ch/puzzle/pcts/architecture/ArchitectureTest.java +++ b/backend/src/test/java/ch/puzzle/pcts/architecture/ArchitectureTest.java @@ -20,6 +20,7 @@ import static com.tngtech.archunit.library.Architectures.layeredArchitecture; import ch.puzzle.pcts.model.Model; +import ch.puzzle.pcts.model.memberoverview.MemberOverview; import com.tngtech.archunit.core.domain.*; import com.tngtech.archunit.core.importer.ClassFileImporter; import com.tngtech.archunit.core.importer.ImportOption; @@ -283,6 +284,8 @@ void modelShouldImplementInterfaceAndOverrideMethods() { .areNotNestedClasses() .and() .haveSimpleNameNotEndingWith("Id") + .and() + .doNotBelongToAnyOf(MemberOverview.class) .should() .beAnnotatedWith(Entity.class) .andShould() From b0c1a6a624c60487c4b10cd68a26eff261aacf42 Mon Sep 17 00:00:00 2001 From: Manuel Date: Fri, 9 Jan 2026 10:41:24 +0100 Subject: [PATCH 23/30] refactor(backend): Remove unwanted dtos and replace them with strings instead #50 --- .../MemberOverviewCertificateDto.java | 2 +- .../MemberOverviewCertificateTypeDto.java | 7 ---- .../degree/MemberOverviewDegreeDto.java | 6 +-- .../degree/MemberOverviewDegreeTypeDto.java | 7 ---- .../MemberOverviewExperienceDto.java | 2 +- .../MemberOverviewExperienceTypeDto.java | 7 ---- .../pcts/mapper/MemberOverviewMapper.java | 14 ++----- .../java/ch/puzzle/pcts/util/TestData.java | 39 +++++++++---------- 8 files changed, 25 insertions(+), 59 deletions(-) delete mode 100644 backend/src/main/java/ch/puzzle/pcts/dto/memberoverview/certificate/MemberOverviewCertificateTypeDto.java delete mode 100644 backend/src/main/java/ch/puzzle/pcts/dto/memberoverview/degree/MemberOverviewDegreeTypeDto.java delete mode 100644 backend/src/main/java/ch/puzzle/pcts/dto/memberoverview/experience/MemberOverviewExperienceTypeDto.java diff --git a/backend/src/main/java/ch/puzzle/pcts/dto/memberoverview/certificate/MemberOverviewCertificateDto.java b/backend/src/main/java/ch/puzzle/pcts/dto/memberoverview/certificate/MemberOverviewCertificateDto.java index 6e85f96eb..16b2d5324 100644 --- a/backend/src/main/java/ch/puzzle/pcts/dto/memberoverview/certificate/MemberOverviewCertificateDto.java +++ b/backend/src/main/java/ch/puzzle/pcts/dto/memberoverview/certificate/MemberOverviewCertificateDto.java @@ -5,7 +5,7 @@ public record MemberOverviewCertificateDto( @Schema(description = "The unique identifier of the member certificate.", example = "1", requiredMode = Schema.RequiredMode.REQUIRED, accessMode = Schema.AccessMode.READ_ONLY, nullable = false) Long id, - @Schema(description = "The type of certificate awarded to the member.", exampleClasses = MemberOverviewCertificateTypeDto.class, requiredMode = Schema.RequiredMode.REQUIRED, nullable = false) MemberOverviewCertificateTypeDto certificate, + @Schema(description = "The name of the certificate-type.", example = "Certified GitOps Associate", requiredMode = Schema.RequiredMode.REQUIRED, minLength = 1) String certificateTypeName, @Schema(description = "The date when the member completed the certificate.", example = "2025-09-24", requiredMode = Schema.RequiredMode.NOT_REQUIRED, nullable = true) LocalDate completedAt, @Schema(description = "An optional comment for the member certificate.", example = "Completed via fast-track program", requiredMode = Schema.RequiredMode.NOT_REQUIRED, nullable = true) String comment) { } diff --git a/backend/src/main/java/ch/puzzle/pcts/dto/memberoverview/certificate/MemberOverviewCertificateTypeDto.java b/backend/src/main/java/ch/puzzle/pcts/dto/memberoverview/certificate/MemberOverviewCertificateTypeDto.java deleted file mode 100644 index 31116763b..000000000 --- a/backend/src/main/java/ch/puzzle/pcts/dto/memberoverview/certificate/MemberOverviewCertificateTypeDto.java +++ /dev/null @@ -1,7 +0,0 @@ -package ch.puzzle.pcts.dto.memberoverview.certificate; - -import io.swagger.v3.oas.annotations.media.Schema; - -public record MemberOverviewCertificateTypeDto( - @Schema(description = "The name of the certificate-type.", example = "Certified GitOps Associate", requiredMode = Schema.RequiredMode.REQUIRED, nullable = false, minLength = 1) String name) { -} diff --git a/backend/src/main/java/ch/puzzle/pcts/dto/memberoverview/degree/MemberOverviewDegreeDto.java b/backend/src/main/java/ch/puzzle/pcts/dto/memberoverview/degree/MemberOverviewDegreeDto.java index f5de5febe..e35c02555 100644 --- a/backend/src/main/java/ch/puzzle/pcts/dto/memberoverview/degree/MemberOverviewDegreeDto.java +++ b/backend/src/main/java/ch/puzzle/pcts/dto/memberoverview/degree/MemberOverviewDegreeDto.java @@ -5,12 +5,8 @@ public record MemberOverviewDegreeDto( @Schema(description = "The unique identifier of the degree.", example = "1", accessMode = Schema.AccessMode.READ_ONLY) Long id, - @Schema(description = "The name of the degree.", example = "Master of Computer Science", requiredMode = Schema.RequiredMode.REQUIRED, minLength = 1) String name, - - @Schema(description = "The type of the degree", exampleClasses = MemberOverviewDegreeTypeDto.class, requiredMode = Schema.RequiredMode.REQUIRED) MemberOverviewDegreeTypeDto type, - + @Schema(description = "The name of the degree-type.", example = "Bachelor of Science", requiredMode = Schema.RequiredMode.REQUIRED, minLength = 1) String degreeTypeName, @Schema(description = "The start date of the degree program.", example = "2018-09-01", requiredMode = Schema.RequiredMode.REQUIRED) LocalDate startDate, - @Schema(description = "The end date of the degree program.", example = "2022-06-30") LocalDate endDate) { } diff --git a/backend/src/main/java/ch/puzzle/pcts/dto/memberoverview/degree/MemberOverviewDegreeTypeDto.java b/backend/src/main/java/ch/puzzle/pcts/dto/memberoverview/degree/MemberOverviewDegreeTypeDto.java deleted file mode 100644 index 5540f28b9..000000000 --- a/backend/src/main/java/ch/puzzle/pcts/dto/memberoverview/degree/MemberOverviewDegreeTypeDto.java +++ /dev/null @@ -1,7 +0,0 @@ -package ch.puzzle.pcts.dto.memberoverview.degree; - -import io.swagger.v3.oas.annotations.media.Schema; - -public record MemberOverviewDegreeTypeDto( - @Schema(description = "The name of the degree-type.", example = "Bachelor of Science", requiredMode = Schema.RequiredMode.REQUIRED, nullable = false, minLength = 1) String name) { -} diff --git a/backend/src/main/java/ch/puzzle/pcts/dto/memberoverview/experience/MemberOverviewExperienceDto.java b/backend/src/main/java/ch/puzzle/pcts/dto/memberoverview/experience/MemberOverviewExperienceDto.java index b84317517..5820b70fc 100644 --- a/backend/src/main/java/ch/puzzle/pcts/dto/memberoverview/experience/MemberOverviewExperienceDto.java +++ b/backend/src/main/java/ch/puzzle/pcts/dto/memberoverview/experience/MemberOverviewExperienceDto.java @@ -7,7 +7,7 @@ public record MemberOverviewExperienceDto( @Schema(description = "The unique identifier of the experience.", example = "1", requiredMode = Schema.RequiredMode.REQUIRED, nullable = false, accessMode = Schema.AccessMode.READ_ONLY) Long id, @Schema(description = "The name or title of the experience", example = "Software Engineer Intern", requiredMode = Schema.RequiredMode.REQUIRED, nullable = false, minLength = 1) String name, @Schema(description = "The employer or organization where the experience took place.", example = "TechCorp Inc.", requiredMode = Schema.RequiredMode.NOT_REQUIRED, nullable = true) String employer, - @Schema(description = "The type or category of the experience.", exampleClasses = MemberOverviewExperienceTypeDto.class, requiredMode = Schema.RequiredMode.REQUIRED, nullable = false) MemberOverviewExperienceTypeDto type, + @Schema(description = "The name of the experience-type.", example = "Management", requiredMode = Schema.RequiredMode.REQUIRED, minLength = 1) String experienceTypeName, @Schema(description = "Additional comments about the experience.", example = "Worked on backend API development using Spring Boot.", requiredMode = Schema.RequiredMode.NOT_REQUIRED, nullable = true) String comment, @Schema(description = "The start date of the experience.", example = "2021-06-01", requiredMode = Schema.RequiredMode.REQUIRED, nullable = false) LocalDate startDate, @Schema(description = "The end date of the experience.", example = "2021-12-31", requiredMode = Schema.RequiredMode.NOT_REQUIRED, nullable = true) LocalDate endDate) { diff --git a/backend/src/main/java/ch/puzzle/pcts/dto/memberoverview/experience/MemberOverviewExperienceTypeDto.java b/backend/src/main/java/ch/puzzle/pcts/dto/memberoverview/experience/MemberOverviewExperienceTypeDto.java deleted file mode 100644 index a565a6eda..000000000 --- a/backend/src/main/java/ch/puzzle/pcts/dto/memberoverview/experience/MemberOverviewExperienceTypeDto.java +++ /dev/null @@ -1,7 +0,0 @@ -package ch.puzzle.pcts.dto.memberoverview.experience; - -import io.swagger.v3.oas.annotations.media.Schema; - -public record MemberOverviewExperienceTypeDto( - @Schema(description = "The name of the experience-type.", example = "Management", requiredMode = Schema.RequiredMode.REQUIRED, nullable = false, minLength = 1) String name) { -} diff --git a/backend/src/main/java/ch/puzzle/pcts/mapper/MemberOverviewMapper.java b/backend/src/main/java/ch/puzzle/pcts/mapper/MemberOverviewMapper.java index 1b1b2ad0e..ee22c9f8b 100644 --- a/backend/src/main/java/ch/puzzle/pcts/mapper/MemberOverviewMapper.java +++ b/backend/src/main/java/ch/puzzle/pcts/mapper/MemberOverviewMapper.java @@ -2,11 +2,8 @@ import ch.puzzle.pcts.dto.memberoverview.*; import ch.puzzle.pcts.dto.memberoverview.certificate.MemberOverviewCertificateDto; -import ch.puzzle.pcts.dto.memberoverview.certificate.MemberOverviewCertificateTypeDto; import ch.puzzle.pcts.dto.memberoverview.degree.MemberOverviewDegreeDto; -import ch.puzzle.pcts.dto.memberoverview.degree.MemberOverviewDegreeTypeDto; import ch.puzzle.pcts.dto.memberoverview.experience.MemberOverviewExperienceDto; -import ch.puzzle.pcts.dto.memberoverview.experience.MemberOverviewExperienceTypeDto; import ch.puzzle.pcts.dto.memberoverview.leadershipexperience.MemberOverviewLeadershipExperienceDto; import ch.puzzle.pcts.dto.memberoverview.leadershipexperience.MemberOverviewLeadershipExperienceTypeDto; import ch.puzzle.pcts.model.memberoverview.MemberOverview; @@ -69,18 +66,15 @@ private MemberOverviewMemberDto getMemberDto(List memberOverview } private MemberOverviewDegreeDto mapToDegree(MemberOverview e) { - return new MemberOverviewDegreeDto(e.getDegreeId(), - e.getDegreeName(), - new MemberOverviewDegreeTypeDto(e.getDegreeTypeName()), - e.getDegreeStartDate(), - e.getDegreeEndDate()); + return new MemberOverviewDegreeDto(e + .getDegreeId(), e.getDegreeName(), e.getDegreeTypeName(), e.getDegreeStartDate(), e.getDegreeEndDate()); } private MemberOverviewExperienceDto mapToExperience(MemberOverview e) { return new MemberOverviewExperienceDto(e.getExperienceId(), e.getExperienceName(), e.getExperienceEmployer(), - new MemberOverviewExperienceTypeDto(e.getExperienceTypeName()), + e.getExperienceTypeName(), e.getExperienceComment(), e.getExperienceStartDate(), e.getExperienceEndDate()); @@ -88,7 +82,7 @@ private MemberOverviewExperienceDto mapToExperience(MemberOverview e) { private MemberOverviewCertificateDto mapToCertificate(MemberOverview e) { return new MemberOverviewCertificateDto(e.getCertificateId(), - new MemberOverviewCertificateTypeDto(e.getCertificateTypeName()), + e.getCertificateTypeName(), e.getCertificateCompletedAt(), e.getCertificateComment()); } diff --git a/backend/src/test/java/ch/puzzle/pcts/util/TestData.java b/backend/src/test/java/ch/puzzle/pcts/util/TestData.java index 0a5699591..611b54ce0 100644 --- a/backend/src/test/java/ch/puzzle/pcts/util/TestData.java +++ b/backend/src/test/java/ch/puzzle/pcts/util/TestData.java @@ -4,11 +4,8 @@ import ch.puzzle.pcts.dto.memberoverview.MemberOverviewDto; import ch.puzzle.pcts.dto.memberoverview.MemberOverviewMemberDto; import ch.puzzle.pcts.dto.memberoverview.certificate.MemberOverviewCertificateDto; -import ch.puzzle.pcts.dto.memberoverview.certificate.MemberOverviewCertificateTypeDto; import ch.puzzle.pcts.dto.memberoverview.degree.MemberOverviewDegreeDto; -import ch.puzzle.pcts.dto.memberoverview.degree.MemberOverviewDegreeTypeDto; import ch.puzzle.pcts.dto.memberoverview.experience.MemberOverviewExperienceDto; -import ch.puzzle.pcts.dto.memberoverview.experience.MemberOverviewExperienceTypeDto; import ch.puzzle.pcts.model.calculation.Calculation; import ch.puzzle.pcts.model.calculation.CalculationState; import ch.puzzle.pcts.model.certificate.Certificate; @@ -467,9 +464,9 @@ private TestData() { .getId(), DEGREE_1 .getName(), - new MemberOverviewDegreeTypeDto(DEGREE_1 + DEGREE_1 .getDegreeType() - .getName()), + .getName(), DEGREE_1 .getStartDate(), DEGREE_1 @@ -481,9 +478,9 @@ private TestData() { .getName(), EXPERIENCE_1 .getEmployer(), - new MemberOverviewExperienceTypeDto(EXPERIENCE_1 + EXPERIENCE_1 .getType() - .getName()), + .getName(), EXPERIENCE_1 .getComment(), EXPERIENCE_1 @@ -496,9 +493,9 @@ private TestData() { .getName(), EXPERIENCE_2 .getEmployer(), - new MemberOverviewExperienceTypeDto(EXPERIENCE_2 + EXPERIENCE_2 .getType() - .getName()), + .getName(), EXPERIENCE_2 .getComment(), EXPERIENCE_2 @@ -508,18 +505,18 @@ private TestData() { List .of(new MemberOverviewCertificateDto(CERTIFICATE_1 .getId(), - new MemberOverviewCertificateTypeDto(CERTIFICATE_1 + CERTIFICATE_1 .getCertificateType() - .getName()), + .getName(), CERTIFICATE_1 .getCompletedAt(), CERTIFICATE_1 .getComment()), new MemberOverviewCertificateDto(CERTIFICATE_4 .getId(), - new MemberOverviewCertificateTypeDto(CERTIFICATE_4 + CERTIFICATE_4 .getCertificateType() - .getName()), + .getName(), CERTIFICATE_4 .getCompletedAt(), CERTIFICATE_4 @@ -549,9 +546,9 @@ private TestData() { .getId(), DEGREE_2 .getName(), - new MemberOverviewDegreeTypeDto(DEGREE_2 + DEGREE_2 .getDegreeType() - .getName()), + .getName(), DEGREE_2 .getStartDate(), DEGREE_2 @@ -563,9 +560,9 @@ private TestData() { .getName(), EXPERIENCE_3 .getEmployer(), - new MemberOverviewExperienceTypeDto(EXPERIENCE_3 + EXPERIENCE_3 .getType() - .getName()), + .getName(), EXPERIENCE_3 .getComment(), EXPERIENCE_3 @@ -575,18 +572,18 @@ private TestData() { List .of(new MemberOverviewCertificateDto(CERTIFICATE_2 .getId(), - new MemberOverviewCertificateTypeDto(CERTIFICATE_2 + CERTIFICATE_2 .getCertificateType() - .getName()), + .getName(), CERTIFICATE_2 .getCompletedAt(), CERTIFICATE_2 .getComment()), new MemberOverviewCertificateDto(CERTIFICATE_3 .getId(), - new MemberOverviewCertificateTypeDto(CERTIFICATE_3 + CERTIFICATE_3 .getCertificateType() - .getName()), + .getName(), CERTIFICATE_3 .getCompletedAt(), CERTIFICATE_3 From 9be2a4406f526b90f9b600d1d7d548e01884dd80 Mon Sep 17 00:00:00 2001 From: Manuel Date: Fri, 9 Jan 2026 10:42:49 +0100 Subject: [PATCH 24/30] fix(persistence): Turn repository private final #50 --- .../service/persistence/MemberOverviewPersistenceService.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/src/main/java/ch/puzzle/pcts/service/persistence/MemberOverviewPersistenceService.java b/backend/src/main/java/ch/puzzle/pcts/service/persistence/MemberOverviewPersistenceService.java index 30aff2afb..c3f4ab845 100644 --- a/backend/src/main/java/ch/puzzle/pcts/service/persistence/MemberOverviewPersistenceService.java +++ b/backend/src/main/java/ch/puzzle/pcts/service/persistence/MemberOverviewPersistenceService.java @@ -8,7 +8,7 @@ @Service public class MemberOverviewPersistenceService { - MemberOverviewRepository repository; + private final MemberOverviewRepository repository; public MemberOverviewPersistenceService(MemberOverviewRepository repository) { this.repository = repository; From 6dfad01e79a2aabeb5d60e9dd5084de8e7b6765b Mon Sep 17 00:00:00 2001 From: Manuel Date: Fri, 9 Jan 2026 11:01:34 +0100 Subject: [PATCH 25/30] refactor(service): Change location of excpetion thrown from business service to persistence service #50 --- .../MemberOverviewBusinessService.java | 29 +------------------ .../MemberOverviewPersistenceService.java | 29 ++++++++++++++++++- 2 files changed, 29 insertions(+), 29 deletions(-) diff --git a/backend/src/main/java/ch/puzzle/pcts/service/business/MemberOverviewBusinessService.java b/backend/src/main/java/ch/puzzle/pcts/service/business/MemberOverviewBusinessService.java index dc843b59f..d63faab97 100644 --- a/backend/src/main/java/ch/puzzle/pcts/service/business/MemberOverviewBusinessService.java +++ b/backend/src/main/java/ch/puzzle/pcts/service/business/MemberOverviewBusinessService.java @@ -1,17 +1,9 @@ package ch.puzzle.pcts.service.business; -import static ch.puzzle.pcts.Constants.MEMBER_OVERVIEW; - -import ch.puzzle.pcts.dto.error.ErrorKey; -import ch.puzzle.pcts.dto.error.FieldKey; -import ch.puzzle.pcts.dto.error.GenericErrorDto; -import ch.puzzle.pcts.exception.PCTSException; import ch.puzzle.pcts.model.memberoverview.MemberOverview; import ch.puzzle.pcts.service.persistence.MemberOverviewPersistenceService; import ch.puzzle.pcts.service.validation.MemberOverviewValidationService; import java.util.List; -import java.util.Map; -import org.springframework.http.HttpStatus; import org.springframework.stereotype.Service; @Service @@ -30,25 +22,6 @@ public MemberOverviewBusinessService(MemberOverviewPersistenceService persistenc public List getById(Long id) { validationService.validateOnGetById(id); - List result = persistenceService.getById(id); - - if (result.isEmpty()) { - throw new PCTSException(HttpStatus.NOT_FOUND, - List - .of(new GenericErrorDto(ErrorKey.NOT_FOUND, - Map - .of(FieldKey.ENTITY, - entityName(), - FieldKey.FIELD, - "id", - FieldKey.IS, - id.toString())))); - } - - return result; - } - - protected String entityName() { - return MEMBER_OVERVIEW; + return persistenceService.getById(id); } } diff --git a/backend/src/main/java/ch/puzzle/pcts/service/persistence/MemberOverviewPersistenceService.java b/backend/src/main/java/ch/puzzle/pcts/service/persistence/MemberOverviewPersistenceService.java index c3f4ab845..2666429bb 100644 --- a/backend/src/main/java/ch/puzzle/pcts/service/persistence/MemberOverviewPersistenceService.java +++ b/backend/src/main/java/ch/puzzle/pcts/service/persistence/MemberOverviewPersistenceService.java @@ -1,8 +1,16 @@ package ch.puzzle.pcts.service.persistence; +import static ch.puzzle.pcts.Constants.MEMBER_OVERVIEW; + +import ch.puzzle.pcts.dto.error.ErrorKey; +import ch.puzzle.pcts.dto.error.FieldKey; +import ch.puzzle.pcts.dto.error.GenericErrorDto; +import ch.puzzle.pcts.exception.PCTSException; import ch.puzzle.pcts.model.memberoverview.MemberOverview; import ch.puzzle.pcts.repository.MemberOverviewRepository; import java.util.List; +import java.util.Map; +import org.springframework.http.HttpStatus; import org.springframework.stereotype.Service; @Service @@ -15,6 +23,25 @@ public MemberOverviewPersistenceService(MemberOverviewRepository repository) { } public List getById(Long id) { - return repository.findAllByMemberId(id); + List result = repository.findAllByMemberId(id); + + if (result.isEmpty()) { + throw new PCTSException(HttpStatus.NOT_FOUND, + List + .of(new GenericErrorDto(ErrorKey.NOT_FOUND, + Map + .of(FieldKey.ENTITY, + entityName(), + FieldKey.FIELD, + "id", + FieldKey.IS, + id.toString())))); + } + + return result; + } + + protected String entityName() { + return MEMBER_OVERVIEW; } } From 1a7420607127415dbd806f751c7acfc5f05b843f Mon Sep 17 00:00:00 2001 From: Manuel Date: Fri, 9 Jan 2026 11:06:37 +0100 Subject: [PATCH 26/30] test(persistence): Check if id exists #50 --- .../MemberOverviewPersistenceServiceIT.java | 28 +++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/backend/src/test/java/ch/puzzle/pcts/service/persistence/MemberOverviewPersistenceServiceIT.java b/backend/src/test/java/ch/puzzle/pcts/service/persistence/MemberOverviewPersistenceServiceIT.java index 18f0246ad..e7f2e8883 100644 --- a/backend/src/test/java/ch/puzzle/pcts/service/persistence/MemberOverviewPersistenceServiceIT.java +++ b/backend/src/test/java/ch/puzzle/pcts/service/persistence/MemberOverviewPersistenceServiceIT.java @@ -2,12 +2,19 @@ import static ch.puzzle.pcts.util.TestData.MEMBER_1_OVERVIEWS; import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; +import ch.puzzle.pcts.dto.error.ErrorKey; +import ch.puzzle.pcts.dto.error.FieldKey; +import ch.puzzle.pcts.exception.PCTSException; import ch.puzzle.pcts.model.memberoverview.MemberOverview; import java.util.List; +import java.util.Map; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.HttpStatus; class MemberOverviewPersistenceServiceIT extends PersistenceCoreIT { @@ -48,4 +55,25 @@ void shouldGetAllMemberOverviewRowsForMember1() { .usingRecursiveFieldByFieldElementComparator() .containsExactlyElementsOf(MEMBER_1_OVERVIEWS); } + + @DisplayName("Should throw exception when id is not found") + @Test + void shouldThrowExceptionWhenIdIsNotFound() { + Long invalidId = -1L; + + Map expectedAttributes = Map + .of(FieldKey.FIELD, + "id", + FieldKey.IS, + String.valueOf(invalidId), + FieldKey.ENTITY, + service.entityName()); + + PCTSException exception = assertThrows(PCTSException.class, () -> service.getById(invalidId)); + + assertEquals(HttpStatus.NOT_FOUND, exception.getStatusCode()); + + assertEquals(List.of(ErrorKey.NOT_FOUND), exception.getErrorKeys()); + assertEquals(List.of(expectedAttributes), exception.getErrorAttributes()); + } } From 258a75c9fc21b5531302d35de42c4a11f77f57f1 Mon Sep 17 00:00:00 2001 From: Manuel Date: Fri, 9 Jan 2026 11:07:29 +0100 Subject: [PATCH 27/30] style(backend): Format code #50 --- .../service/persistence/CertificatePersistenceServiceIT.java | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/backend/src/test/java/ch/puzzle/pcts/service/persistence/CertificatePersistenceServiceIT.java b/backend/src/test/java/ch/puzzle/pcts/service/persistence/CertificatePersistenceServiceIT.java index b0beef188..c1cf4795f 100644 --- a/backend/src/test/java/ch/puzzle/pcts/service/persistence/CertificatePersistenceServiceIT.java +++ b/backend/src/test/java/ch/puzzle/pcts/service/persistence/CertificatePersistenceServiceIT.java @@ -1,7 +1,7 @@ package ch.puzzle.pcts.service.persistence; -import static org.junit.jupiter.api.Assertions.*; import static ch.puzzle.pcts.util.TestData.*; +import static org.junit.jupiter.api.Assertions.*; import ch.puzzle.pcts.model.certificate.Certificate; import ch.puzzle.pcts.model.certificatetype.CertificateKind; @@ -17,8 +17,6 @@ import java.math.BigDecimal; import java.time.LocalDate; import java.util.List; -import java.util.Optional; -import java.util.Set; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; From f99a6ade5116c164db7ca5a132c6a616c12486aa Mon Sep 17 00:00:00 2001 From: Manuel Date: Fri, 9 Jan 2026 11:19:37 +0100 Subject: [PATCH 28/30] test(persistence): Fix small assertion mistake in test #50 --- .../service/persistence/MemberOverviewPersistenceServiceIT.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/src/test/java/ch/puzzle/pcts/service/persistence/MemberOverviewPersistenceServiceIT.java b/backend/src/test/java/ch/puzzle/pcts/service/persistence/MemberOverviewPersistenceServiceIT.java index e7f2e8883..13e81bb7e 100644 --- a/backend/src/test/java/ch/puzzle/pcts/service/persistence/MemberOverviewPersistenceServiceIT.java +++ b/backend/src/test/java/ch/puzzle/pcts/service/persistence/MemberOverviewPersistenceServiceIT.java @@ -53,7 +53,7 @@ void shouldGetAllMemberOverviewRowsForMember1() { assertThat(memberOverviews) .usingRecursiveFieldByFieldElementComparator() - .containsExactlyElementsOf(MEMBER_1_OVERVIEWS); + .containsExactlyInAnyOrderElementsOf(MEMBER_1_OVERVIEWS); } @DisplayName("Should throw exception when id is not found") From 88fcf7afc2b86d118c1cbf20ad502a84902db145 Mon Sep 17 00:00:00 2001 From: Manuel Date: Fri, 9 Jan 2026 16:33:20 +0100 Subject: [PATCH 29/30] refactor: Resolve conversations #50 --- .../pcts/mapper/MemberOverviewMapper.java | 9 +++--- .../model/memberoverview/MemberOverview.java | 28 ++++++++++--------- .../memberoverview/MemberOverviewId.java | 3 +- .../MemberOverviewValidationService.java | 1 + .../pcts/architecture/ArchitectureTest.java | 9 ++---- .../MemberOverviewPersistenceServiceIT.java | 2 +- 6 files changed, 25 insertions(+), 27 deletions(-) diff --git a/backend/src/main/java/ch/puzzle/pcts/mapper/MemberOverviewMapper.java b/backend/src/main/java/ch/puzzle/pcts/mapper/MemberOverviewMapper.java index ee22c9f8b..91ca35ab9 100644 --- a/backend/src/main/java/ch/puzzle/pcts/mapper/MemberOverviewMapper.java +++ b/backend/src/main/java/ch/puzzle/pcts/mapper/MemberOverviewMapper.java @@ -7,7 +7,6 @@ import ch.puzzle.pcts.dto.memberoverview.leadershipexperience.MemberOverviewLeadershipExperienceDto; import ch.puzzle.pcts.dto.memberoverview.leadershipexperience.MemberOverviewLeadershipExperienceTypeDto; import ch.puzzle.pcts.model.memberoverview.MemberOverview; -import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -45,10 +44,10 @@ public MemberOverviewDto toDto(List memberOverviews) { } } - MemberOverviewCvDto cvDto = new MemberOverviewCvDto(new ArrayList<>(degreeMap.values()), - new ArrayList<>(experienceMap.values()), - new ArrayList<>(certificateMap.values()), - new ArrayList<>(leadershipMap.values())); + MemberOverviewCvDto cvDto = new MemberOverviewCvDto(List.copyOf(degreeMap.values()), + List.copyOf(experienceMap.values()), + List.copyOf(certificateMap.values()), + List.copyOf(leadershipMap.values())); return new MemberOverviewDto(getMemberDto(memberOverviews), cvDto); } diff --git a/backend/src/main/java/ch/puzzle/pcts/model/memberoverview/MemberOverview.java b/backend/src/main/java/ch/puzzle/pcts/model/memberoverview/MemberOverview.java index acfe6a924..17a625be5 100644 --- a/backend/src/main/java/ch/puzzle/pcts/model/memberoverview/MemberOverview.java +++ b/backend/src/main/java/ch/puzzle/pcts/model/memberoverview/MemberOverview.java @@ -286,8 +286,9 @@ public void setExperienceTypeName(String experienceTypeName) { @Override public boolean equals(Object o) { - if (!(o instanceof MemberOverview that)) + if (!(o instanceof MemberOverview that)) { return false; + } return Objects.equals(getMemberId(), that.getMemberId()) && Objects.equals(getFirstName(), that.getFirstName()) && Objects.equals(getLastName(), that.getLastName()) && Objects.equals(getAbbreviation(), that.getAbbreviation()) @@ -346,18 +347,19 @@ public int hashCode() { @Override public String toString() { - return "MemberOverview{" + "memberId=" + memberId + ", firstName='" + firstName + '\'' + ", lastName='" - + lastName + '\'' + ", abbreviation='" + abbreviation + '\'' + ", birthDate=" + birthDate - + ", dateOfHire=" + dateOfHire + ", employmentState=" + employmentState + ", organisationUnitName='" - + organisationUnitName + '\'' + ", certificateId=" + certificateId + ", certificateCompletedAt=" - + certificateCompletedAt + ", certificateComment='" + certificateComment + '\'' - + ", certificateTypeName='" + certificateTypeName + '\'' + ", leadershipTypeKind=" + leadershipTypeKind - + ", degreeId=" + degreeId + ", degreeName='" + degreeName + '\'' + ", degreeStartDate=" - + degreeStartDate + ", degreeEndDate=" + degreeEndDate + ", degreeTypeName='" + degreeTypeName + '\'' - + ", experienceId=" + experienceId + ", experienceName='" + experienceName + '\'' - + ", experienceEmployer='" + experienceEmployer + '\'' + ", experienceStartDate=" + experienceStartDate - + ", experienceEndDate=" + experienceEndDate + ", experienceComment='" + experienceComment + '\'' - + ", experienceTypeName='" + experienceTypeName + '\'' + '}'; + return "MemberOverview{" + "memberId=" + getMemberId() + ", firstName='" + getFirstName() + '\'' + + ", lastName='" + getLastName() + '\'' + ", abbreviation='" + getAbbreviation() + '\'' + ", birthDate=" + + getBirthDate() + ", dateOfHire=" + getDateOfHire() + ", employmentState=" + getEmploymentState() + + ", organisationUnitName='" + getOrganisationUnitName() + '\'' + ", certificateId=" + getCertificateId() + + ", certificateCompletedAt=" + getCertificateCompletedAt() + ", certificateComment='" + + getCertificateComment() + '\'' + ", certificateTypeName='" + getCertificateTypeName() + '\'' + + ", leadershipTypeKind=" + getLeadershipTypeKind() + ", degreeId=" + getDegreeId() + ", degreeName='" + + getDegreeName() + '\'' + ", degreeStartDate=" + getDegreeStartDate() + ", degreeEndDate=" + + getDegreeEndDate() + ", degreeTypeName='" + getDegreeTypeName() + '\'' + ", experienceId=" + + getExperienceId() + ", experienceName='" + getExperienceName() + '\'' + ", experienceEmployer='" + + getExperienceEmployer() + '\'' + ", experienceStartDate=" + getExperienceStartDate() + + ", experienceEndDate=" + getExperienceEndDate() + ", experienceComment='" + getExperienceComment() + + '\'' + ", experienceTypeName='" + getExperienceTypeName() + '\'' + '}'; } public static final class Builder { diff --git a/backend/src/main/java/ch/puzzle/pcts/model/memberoverview/MemberOverviewId.java b/backend/src/main/java/ch/puzzle/pcts/model/memberoverview/MemberOverviewId.java index 1c1270f1b..9a2155f5e 100644 --- a/backend/src/main/java/ch/puzzle/pcts/model/memberoverview/MemberOverviewId.java +++ b/backend/src/main/java/ch/puzzle/pcts/model/memberoverview/MemberOverviewId.java @@ -54,8 +54,9 @@ public void setExperienceId(Long experienceId) { @Override public boolean equals(Object o) { - if (!(o instanceof MemberOverviewId that)) + if (!(o instanceof MemberOverviewId that)) { return false; + } return Objects.equals(getMemberId(), that.getMemberId()) && Objects.equals(getCertificateId(), that.getCertificateId()) && Objects.equals(getDegreeId(), that.getDegreeId()) diff --git a/backend/src/main/java/ch/puzzle/pcts/service/validation/MemberOverviewValidationService.java b/backend/src/main/java/ch/puzzle/pcts/service/validation/MemberOverviewValidationService.java index 93d59db55..ede852722 100644 --- a/backend/src/main/java/ch/puzzle/pcts/service/validation/MemberOverviewValidationService.java +++ b/backend/src/main/java/ch/puzzle/pcts/service/validation/MemberOverviewValidationService.java @@ -9,6 +9,7 @@ import org.springframework.http.HttpStatus; import org.springframework.stereotype.Service; +// Does not inherit from ValidationBase due to the model not extending from the base either, which is due to the primary key being a composite key @Service public class MemberOverviewValidationService { diff --git a/backend/src/test/java/ch/puzzle/pcts/architecture/ArchitectureTest.java b/backend/src/test/java/ch/puzzle/pcts/architecture/ArchitectureTest.java index 2b41d99ec..f284dc8a3 100644 --- a/backend/src/test/java/ch/puzzle/pcts/architecture/ArchitectureTest.java +++ b/backend/src/test/java/ch/puzzle/pcts/architecture/ArchitectureTest.java @@ -11,8 +11,7 @@ import static ch.puzzle.pcts.architecture.condition.ClassConditions.overrideToStringMethod; import static ch.puzzle.pcts.architecture.condition.CodeUnitConditions.trimAssignedStringFields; import static com.tngtech.archunit.base.DescribedPredicate.not; -import static com.tngtech.archunit.core.domain.JavaClass.Predicates.resideInAPackage; -import static com.tngtech.archunit.core.domain.JavaClass.Predicates.type; +import static com.tngtech.archunit.core.domain.JavaClass.Predicates.*; import static com.tngtech.archunit.lang.conditions.ArchConditions.and; import static com.tngtech.archunit.lang.syntax.ArchRuleDefinition.*; import static com.tngtech.archunit.lang.syntax.ArchRuleDefinition.classes; @@ -20,7 +19,6 @@ import static com.tngtech.archunit.library.Architectures.layeredArchitecture; import ch.puzzle.pcts.model.Model; -import ch.puzzle.pcts.model.memberoverview.MemberOverview; import com.tngtech.archunit.core.domain.*; import com.tngtech.archunit.core.importer.ClassFileImporter; import com.tngtech.archunit.core.importer.ImportOption; @@ -276,16 +274,13 @@ void modelShouldImplementInterfaceAndOverrideMethods() { ArchRule rule = classes() .that(resideInAPackage("ch.puzzle.pcts.model..")) + .and(not(resideInAnyPackage("ch.puzzle.pcts.model.memberoverview.."))) .and() .areNotInterfaces() .and() .areNotEnums() .and() .areNotNestedClasses() - .and() - .haveSimpleNameNotEndingWith("Id") - .and() - .doNotBelongToAnyOf(MemberOverview.class) .should() .beAnnotatedWith(Entity.class) .andShould() diff --git a/backend/src/test/java/ch/puzzle/pcts/service/persistence/MemberOverviewPersistenceServiceIT.java b/backend/src/test/java/ch/puzzle/pcts/service/persistence/MemberOverviewPersistenceServiceIT.java index 13e81bb7e..4421287e6 100644 --- a/backend/src/test/java/ch/puzzle/pcts/service/persistence/MemberOverviewPersistenceServiceIT.java +++ b/backend/src/test/java/ch/puzzle/pcts/service/persistence/MemberOverviewPersistenceServiceIT.java @@ -18,7 +18,7 @@ class MemberOverviewPersistenceServiceIT extends PersistenceCoreIT { - protected final MemberOverviewPersistenceService service; + private final MemberOverviewPersistenceService service; @Autowired MemberOverviewPersistenceServiceIT(MemberOverviewPersistenceService service) { From a48156fac818b70fcb1749c011260b2280a86684 Mon Sep 17 00:00:00 2001 From: Manuel Date: Tue, 13 Jan 2026 09:16:29 +0100 Subject: [PATCH 30/30] test(Architecture): Split models tests into three to allow more precision #50 --- .../pcts/architecture/ArchitectureTest.java | 52 ++++++++++++++++--- 1 file changed, 44 insertions(+), 8 deletions(-) diff --git a/backend/src/test/java/ch/puzzle/pcts/architecture/ArchitectureTest.java b/backend/src/test/java/ch/puzzle/pcts/architecture/ArchitectureTest.java index f284dc8a3..d6de5601d 100644 --- a/backend/src/test/java/ch/puzzle/pcts/architecture/ArchitectureTest.java +++ b/backend/src/test/java/ch/puzzle/pcts/architecture/ArchitectureTest.java @@ -267,31 +267,67 @@ void dtoShouldBeRecord() { rule.check(importedClasses); } - @DisplayName("Models should implement interface, have @Entity annotation and override methods") + @DisplayName("Models should override methods") @Test - void modelShouldImplementInterfaceAndOverrideMethods() { + void modelShouldOverrideMethods() { JavaClasses importedClasses = getMainSourceClasses(); ArchRule rule = classes() .that(resideInAPackage("ch.puzzle.pcts.model..")) - .and(not(resideInAnyPackage("ch.puzzle.pcts.model.memberoverview.."))) .and() .areNotInterfaces() .and() .areNotEnums() .and() .areNotNestedClasses() - .should() - .beAnnotatedWith(Entity.class) - .andShould() - .implement(Model.class) - .andShould(overrideEqualsMethod) + .should(overrideEqualsMethod) .andShould(overrideHashCodeMethod) .andShould(overrideToStringMethod); rule.check(importedClasses); } + @DisplayName("Models should have @Entity annotation") + @Test + void modelShouldHaveEntityAnnotation() { + JavaClasses importedClasses = getMainSourceClasses(); + + ArchRule rule = classes() + .that(resideInAPackage("ch.puzzle.pcts.model..")) + .and() + .doNotHaveSimpleName("MemberOverviewId") + .and() + .areNotInterfaces() + .and() + .areNotEnums() + .and() + .areNotNestedClasses() + .should() + .beAnnotatedWith(Entity.class); + + rule.check(importedClasses); + } + + @DisplayName("Models should implement interface") + @Test + void modelShouldImplementInterface() { + JavaClasses importedClasses = getMainSourceClasses(); + + ArchRule rule = classes() + .that(resideInAPackage("ch.puzzle.pcts.model..")) + .and(not(resideInAnyPackage("ch.puzzle.pcts.model.memberoverview.."))) + .and() + .areNotInterfaces() + .and() + .areNotEnums() + .and() + .areNotNestedClasses() + .should() + .implement(Model.class); + + rule.check(importedClasses); + } + @DisplayName("Classes should reside in the correct packages based on naming conventions") @ParameterizedTest @ValueSource(strings = { "controller", "service", "mapper", "repository", "dto", "exception" })