Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
40e635b
feat(db): Add basic structure for db view #50
ManuelMoeri Dec 5, 2025
7c98a2d
feat(backend): Add endpoint for the overview #50
ManuelMoeri Dec 8, 2025
5226b4c
feat(backend): Add leadershipExperiences and calculation endpoint to …
ManuelMoeri Dec 9, 2025
743c127
refactor(backend): Utilize objects instead of pure json when creating…
ManuelMoeri Dec 10, 2025
f1892a9
fix(backend): Re-write view to not collapse every entitiy into one #50
ManuelMoeri Dec 15, 2025
c21824e
test(backend): Fix existing test by adding trim to every string #50
ManuelMoeri Dec 15, 2025
80dc520
refactor(MemberOverview): Remove unwanted attribute #50
ManuelMoeri Dec 15, 2025
b3d5020
refactor(backend): Size down endpoint to only hold the necessary data…
ManuelMoeri Dec 16, 2025
f1d0826
refactor(dto): Add overview dtos for only the needed attributes #50
ManuelMoeri Dec 19, 2025
5e66b77
refactor(backend): Fix mapper and model to actually work for now #50
ManuelMoeri Dec 19, 2025
6f8904d
test: add a prototype of unified testData #50
nevio18324 Dec 22, 2025
f927a86
test: add tests for mapper and controller #50
nevio18324 Dec 23, 2025
c751a41
refactor: format code #50
nevio18324 Dec 23, 2025
4c0cb75
refactor: import static constants instead of accessing via class #50
nevio18324 Dec 23, 2025
cb020b1
refactor(memberoverview): Remove unwanted attributes and make small q…
ManuelMoeri Jan 7, 2026
a9654ed
test(backend): Fix corresponding tests and test data #50
ManuelMoeri Jan 7, 2026
fe19230
refactor(controller): Clarify naming #50
ManuelMoeri Jan 7, 2026
bdfb672
test(mapper): Add additional tests #50
ManuelMoeri Jan 7, 2026
16e0296
refactor(db): Use java composite primary key instead of rowid #50
ManuelMoeri Jan 9, 2026
e4ad548
refactor(dto): Fix name of dto to have correct prefix #50
ManuelMoeri Jan 9, 2026
89134d4
refactor(dto): Re-name other occurences of dto #50
ManuelMoeri Jan 9, 2026
1525437
fix(model): Remove inheritation of model #50
ManuelMoeri Jan 9, 2026
b0c1a6a
refactor(backend): Remove unwanted dtos and replace them with strings…
ManuelMoeri Jan 9, 2026
9be2a44
fix(persistence): Turn repository private final #50
ManuelMoeri Jan 9, 2026
6dfad01
refactor(service): Change location of excpetion thrown from business …
ManuelMoeri Jan 9, 2026
1a74206
test(persistence): Check if id exists #50
ManuelMoeri Jan 9, 2026
258a75c
style(backend): Format code #50
ManuelMoeri Jan 9, 2026
f99a6ad
test(persistence): Fix small assertion mistake in test #50
ManuelMoeri Jan 9, 2026
88fcf7a
refactor: Resolve conversations #50
ManuelMoeri Jan 9, 2026
a48156f
test(Architecture): Split models tests into three to allow more preci…
ManuelMoeri Jan 13, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions backend/src/main/java/ch/puzzle/pcts/Constants.java
Original file line number Diff line number Diff line change
Expand Up @@ -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() {
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
package ch.puzzle.pcts.controller;

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 java.util.List;
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 the member")
public class MemberOverviewController {
private final MemberOverviewMapper memberOverviewMapper;
private final MemberOverviewBusinessService memberOverviewBusinessService;

public MemberOverviewController(MemberOverviewMapper mapper, MemberOverviewBusinessService service) {
this.memberOverviewMapper = mapper;
this.memberOverviewBusinessService = service;
}

@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 = MemberOverviewDto.class)) })
@GetMapping("{memberId}")
public ResponseEntity<MemberOverviewDto> getMemberOverviewByMemberId(@Parameter(description = "The ID of the member whose overview should be retrieved.", required = true)
@PathVariable Long memberId) {
List<MemberOverview> memberOverviews = memberOverviewBusinessService.getById(memberId);
return ResponseEntity.ok(memberOverviewMapper.toDto(memberOverviews));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package ch.puzzle.pcts.dto.memberoverview;

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 MemberOverviewCvDto(List<MemberOverviewDegreeDto> degrees, List<MemberOverviewExperienceDto> experiences,
List<MemberOverviewCertificateDto> certificates,
List<MemberOverviewLeadershipExperienceDto> leadershipExperiences) {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
package ch.puzzle.pcts.dto.memberoverview;

public record MemberOverviewDto(MemberOverviewMemberDto member, MemberOverviewCvDto cv) {
}
Original file line number Diff line number Diff line change
@@ -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) {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
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,
Comment thread
ManuelMoeri marked this conversation as resolved.
@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) {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
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 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) {
}
Original file line number Diff line number Diff line change
@@ -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,
Comment thread
lcanobbio marked this conversation as resolved.
@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 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) {
}
Original file line number Diff line number Diff line change
@@ -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,
Comment thread
lcanobbio marked this conversation as resolved.

@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) {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
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 = "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) {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
package ch.puzzle.pcts.mapper;

import ch.puzzle.pcts.dto.memberoverview.*;
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 ch.puzzle.pcts.dto.memberoverview.leadershipexperience.MemberOverviewLeadershipExperienceTypeDto;
import ch.puzzle.pcts.model.memberoverview.MemberOverview;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.springframework.stereotype.Component;

@Component
public class MemberOverviewMapper {

public MemberOverviewDto toDto(List<MemberOverview> memberOverviews) {
if (memberOverviews == null || memberOverviews.isEmpty()) {
return null;
}

Map<Long, MemberOverviewDegreeDto> degreeMap = new HashMap<>();
Map<Long, MemberOverviewExperienceDto> experienceMap = new HashMap<>();
Map<Long, MemberOverviewCertificateDto> certificateMap = new HashMap<>();
Map<Long, MemberOverviewLeadershipExperienceDto> leadershipMap = new HashMap<>();

for (MemberOverview row : memberOverviews) {

if (row.getDegreeId() != null) {
degreeMap.putIfAbsent(row.getDegreeId(), mapToDegree(row));
}

if (row.getExperienceId() != null) {
experienceMap.putIfAbsent(row.getExperienceId(), mapToExperience(row));
}

if (row.getCertificateId() != null) {
if (row.getLeadershipTypeKind().isLeadershipExperienceType()) {
leadershipMap.putIfAbsent(row.getCertificateId(), mapToLeadershipExperience(row));
} else {
certificateMap.putIfAbsent(row.getCertificateId(), mapToCertificate(row));
}
}
}

MemberOverviewCvDto cvDto = new MemberOverviewCvDto(List.copyOf(degreeMap.values()),
Comment thread
kcinay055679 marked this conversation as resolved.
List.copyOf(experienceMap.values()),
List.copyOf(certificateMap.values()),
List.copyOf(leadershipMap.values()));

return new MemberOverviewDto(getMemberDto(memberOverviews), cvDto);
}

private MemberOverviewMemberDto getMemberDto(List<MemberOverview> memberOverviews) {
MemberOverview first = memberOverviews.getFirst();
return new MemberOverviewMemberDto(first.getMemberId(),
first.getFirstName(),
first.getLastName(),
first.getEmploymentState(),
first.getAbbreviation(),
first.getDateOfHire(),
first.getBirthDate(),
first.getOrganisationUnitName());
}

private MemberOverviewDegreeDto mapToDegree(MemberOverview e) {
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(),
e.getExperienceTypeName(),
e.getExperienceComment(),
e.getExperienceStartDate(),
e.getExperienceEndDate());
}

private MemberOverviewCertificateDto mapToCertificate(MemberOverview e) {
return new MemberOverviewCertificateDto(e.getCertificateId(),
e.getCertificateTypeName(),
e.getCertificateCompletedAt(),
e.getCertificateComment());
}

private MemberOverviewLeadershipExperienceDto mapToLeadershipExperience(MemberOverview e) {
return new MemberOverviewLeadershipExperienceDto(e.getCertificateId(),
new MemberOverviewLeadershipExperienceTypeDto(e
.getCertificateTypeName(), e.getLeadershipTypeKind()),
e.getCertificateComment());
}
}
Loading
Loading