diff --git a/api-request/auth-service/changeEmail.http b/api-request/auth-service/change-email.http similarity index 100% rename from api-request/auth-service/changeEmail.http rename to api-request/auth-service/change-email.http diff --git a/api-request/auth-service/changePassword.http b/api-request/auth-service/change-password.http similarity index 100% rename from api-request/auth-service/changePassword.http rename to api-request/auth-service/change-password.http diff --git a/api-request/auth-service/login.http b/api-request/auth-service/login.http index 082ff99..c55771f 100644 --- a/api-request/auth-service/login.http +++ b/api-request/auth-service/login.http @@ -3,7 +3,7 @@ POST http://localhost:4004/api/auth/login Content-Type: application/json { - "email": "test_email@gmail.com", + "email": "test_email3@gmail.com", "password": "c++_4_life!" } diff --git a/api-request/auth-service/register.http b/api-request/auth-service/register.http index daed131..dc0a690 100644 --- a/api-request/auth-service/register.http +++ b/api-request/auth-service/register.http @@ -2,6 +2,6 @@ POST http://localhost:4004/api/auth/register Content-Type: application/json { - "email": "test_email@gmail.com", + "email": "test_email3@gmail.com", "password": "c++_4_life!" } diff --git a/api-request/client-service/clients/contract/get-pending-agreements.http b/api-request/client-service/clients/contract/get-pending-agreements.http new file mode 100644 index 0000000..e69de29 diff --git a/api-request/client-service/clients/contract/set-acceptance-status-of-pending-agreement.http b/api-request/client-service/clients/contract/set-acceptance-status-of-pending-agreement.http new file mode 100644 index 0000000..e69de29 diff --git a/api-request/client-service/clients/get-clients-coaches.http b/api-request/client-service/clients/get-clients-coaches.http new file mode 100644 index 0000000..2e1a029 --- /dev/null +++ b/api-request/client-service/clients/get-clients-coaches.http @@ -0,0 +1,2 @@ +GET http://localhost:4004/api/clients/client/coaches +Authorization: Bearer {{token}} \ No newline at end of file diff --git a/api-request/client-service/clients/schedule/add-workout-to-schedule.http b/api-request/client-service/clients/schedule/add-workout-to-schedule.http new file mode 100644 index 0000000..209f3df --- /dev/null +++ b/api-request/client-service/clients/schedule/add-workout-to-schedule.http @@ -0,0 +1,23 @@ +POST http://localhost:4004/api/clients/client/schedule/workout +Authorization: Bearer {{token}} +Content-Type: application/json + + +{ + "exercises" : [ + { + "movement" : { + "id" : "{{created_movement_id}}" + }, + "numSets" : "3", + "numReps" : "10", + "coachNotes" : "Take these easy if you need to." + } + ], + "date" : "2025-10-20", + "workoutNotes" : "Hey this is the note. Do good.", + "isCompleted" : "False", + "trainingScheduleId" : "" +} + + diff --git a/api-request/client-service/clients/schedule/delete-workout-from-schedule.http b/api-request/client-service/clients/schedule/delete-workout-from-schedule.http new file mode 100644 index 0000000..7bc64f3 --- /dev/null +++ b/api-request/client-service/clients/schedule/delete-workout-from-schedule.http @@ -0,0 +1,2 @@ +DELETE http://localhost:4004/api/clients/client/schedule/workout/fde2ca82-1a26-4767-adc9-8fbbdfc119e0 +Authorization: Bearer {{token}} \ No newline at end of file diff --git a/api-request/client-service/clients/schedule/get-training-schedule.http b/api-request/client-service/clients/schedule/get-training-schedule.http new file mode 100644 index 0000000..c26dc70 --- /dev/null +++ b/api-request/client-service/clients/schedule/get-training-schedule.http @@ -0,0 +1,5 @@ +### Get request to get a users training schedule. +GET http://localhost:4004/api/clients/client/schedule +Authorization: Bearer {{token}} + +> {% client.global.set("training_schedule_id", response.body.id) %} diff --git a/api-request/client-service/coaches/contract/propose-client-agreement.http b/api-request/client-service/coaches/contract/propose-client-agreement.http new file mode 100644 index 0000000..e69de29 diff --git a/api-request/client-service/coaches/contract/template/create-agreement-template.http b/api-request/client-service/coaches/contract/template/create-agreement-template.http new file mode 100644 index 0000000..e69de29 diff --git a/api-request/client-service/coaches/contract/template/get-agreement-templates.http b/api-request/client-service/coaches/contract/template/get-agreement-templates.http new file mode 100644 index 0000000..e69de29 diff --git a/api-request/client-service/coaches/contract/withdraw-coach-agreement-acceptance.http b/api-request/client-service/coaches/contract/withdraw-coach-agreement-acceptance.http new file mode 100644 index 0000000..e69de29 diff --git a/api-request/client-service/coaches/get-clients.http b/api-request/client-service/coaches/get-clients.http new file mode 100644 index 0000000..e69de29 diff --git a/api-request/client-service/coaches/schedule/add-workout-to-client-schedule.http b/api-request/client-service/coaches/schedule/add-workout-to-client-schedule.http new file mode 100644 index 0000000..e69de29 diff --git a/api-request/client-service/coaches/schedule/delete-workout-from-client-schedule.http b/api-request/client-service/coaches/schedule/delete-workout-from-client-schedule.http new file mode 100644 index 0000000..e69de29 diff --git a/api-request/client-service/coaches/schedule/get-client-schedule.http b/api-request/client-service/coaches/schedule/get-client-schedule.http new file mode 100644 index 0000000..e69de29 diff --git a/api-request/client-service/create-user.http b/api-request/client-service/create-user.http index 3e54968..d8610da 100644 --- a/api-request/client-service/create-user.http +++ b/api-request/client-service/create-user.http @@ -1,13 +1,12 @@ ### Post request to create a user. -POST http://localhost:4004/api/clients +POST http://localhost:4004/api/clients/users Authorization: Bearer {{token}} -X-AUTH-ID: a7920032-59cf-461f-a293-6944954d3747 Content-Type: application/json { "firstName": "Test", "lastName": "User", - "email": "test_user@test.com", + "email": "test_user3@test.com", "address": "123 Test-Request Ave, 493201, WA, USA.", "dateOfBirth": "2000-01-01", "registeredDate": "2025-01-01" diff --git a/api-request/client-service/delete-user.http b/api-request/client-service/delete-user.http index 552427c..2072e0f 100644 --- a/api-request/client-service/delete-user.http +++ b/api-request/client-service/delete-user.http @@ -1,2 +1,2 @@ -DELETE http://localhost:4004/api/clients/a526c437-cd19-48a2-9804-fdbe091c0b38 +DELETE http://localhost:4004/api/clients/users/a526c437-cd19-48a2-9804-fdbe091c0b38 Authorization: Bearer {{token}} diff --git a/api-request/client-service/get-users.http b/api-request/client-service/get-users.http index 5b47f54..038efc5 100644 --- a/api-request/client-service/get-users.http +++ b/api-request/client-service/get-users.http @@ -1,3 +1,3 @@ ### GET request to get the users. -GET http://localhost:4004/api/clients +GET http://localhost:4004/api/clients/users Authorization: Bearer {{token}} \ No newline at end of file diff --git a/api-request/client-service/movements/create-movement.http b/api-request/client-service/movements/create-movement.http new file mode 100644 index 0000000..fbbb056 --- /dev/null +++ b/api-request/client-service/movements/create-movement.http @@ -0,0 +1,10 @@ +POST http://localhost:4004/api/clients/movements +Authorization: Bearer {{token}} +Content-Type: application/json + +{ + "name" : "Deadlift", + "description" : "Sumo or bust." +} + +> {% client.global.set("created_movement_id", response.body.id) %} diff --git a/api-request/client-service/movements/get-movement.http b/api-request/client-service/movements/get-movement.http new file mode 100644 index 0000000..ac56bf3 --- /dev/null +++ b/api-request/client-service/movements/get-movement.http @@ -0,0 +1,2 @@ +GET http://localhost:4004/api/clients/movements/{{created_movement_id}} +Authorization: Bearer {{token}} diff --git a/client-service/Dockerfile b/client-service/Dockerfile index 96fe42e..6dd2232 100644 --- a/client-service/Dockerfile +++ b/client-service/Dockerfile @@ -8,7 +8,7 @@ RUN mvn dependency:go-offline -B COPY src ./src -RUN mvn clean package +RUN mvn clean package -DskipTests FROM openjdk:22-jdk AS runner diff --git a/client-service/pom.xml b/client-service/pom.xml index 4b74450..0784580 100644 --- a/client-service/pom.xml +++ b/client-service/pom.xml @@ -49,11 +49,7 @@ runtime true - - org.postgresql - postgresql - runtime - + org.springframework.boot spring-boot-starter-test @@ -84,9 +80,34 @@ jakarta.persistence-api 3.1.0 + + org.postgresql + postgresql + runtime + + + org.testcontainers + junit-jupiter + test + + + org.testcontainers + postgresql + test + - + + io.rest-assured + rest-assured + test + + + org.testcontainers + junit-jupiter + test + + @@ -97,4 +118,5 @@ + diff --git a/client-service/src/main/java/com/cm/clientservice/controller/ClientController.java b/client-service/src/main/java/com/cm/clientservice/controller/ClientController.java index f7d789f..4092f4d 100644 --- a/client-service/src/main/java/com/cm/clientservice/controller/ClientController.java +++ b/client-service/src/main/java/com/cm/clientservice/controller/ClientController.java @@ -1,17 +1,20 @@ package com.cm.clientservice.controller; - import com.cm.clientservice.dto.UserResponseDTO; +import com.cm.clientservice.dto.contract.CoachClientAgreementResponseDto; +import com.cm.clientservice.dto.contract.ContractAgreementStatusRequestDto; +import com.cm.clientservice.dto.scheduling.TrainingScheduleDto; +import com.cm.clientservice.dto.scheduling.workout.WorkoutRequestDto; import com.cm.clientservice.service.UserService; -import io.swagger.v3.oas.annotations.Operation; +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.RequestMapping; -import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.bind.annotation.*; import java.util.List; +import java.util.UUID; @RestController -@RequestMapping("/clients") +@RequestMapping("clients/client") +@Tag(name="client", description = "API for clients to manage their experience.") public class ClientController { private final UserService userService; @@ -21,9 +24,51 @@ public ClientController(UserService userService){ } @GetMapping("/coaches") - @Operation(summary = "Get all users") - public ResponseEntity> getAllCoachUsers(){ - List users = userService.getAllCoachUsers(); - return ResponseEntity.ok().body(users); + public ResponseEntity> getClientsCoaches(@RequestHeader("X-AUTH-ID") String authId){ + List coaches = userService.getCoachesOfClient(UUID.fromString(authId)); + return ResponseEntity.ok().body(coaches); + } + + @GetMapping("/schedule") + public ResponseEntity getTrainingSchedule(@RequestHeader("X-AUTH-ID") String authId){ + TrainingScheduleDto trainingScheduleDto = userService.getTrainingSchedule(UUID.fromString(authId)); + return ResponseEntity.ok().body(trainingScheduleDto); + } + + @DeleteMapping("/schedule/workout/{workoutId}") + public ResponseEntity deleteWorkoutFromSchedule(@RequestHeader("X-AUTH-ID") String authId, + @PathVariable String workoutId){ + + TrainingScheduleDto trainingScheduleDto = + userService.removeWorkoutFromSchedule(UUID.fromString(authId), UUID.fromString(workoutId)); + + return ResponseEntity.ok().body(trainingScheduleDto); + } + + @PostMapping("/schedule/workout") + public ResponseEntity addWorkoutToSchedule(@RequestHeader("X-AUTH-ID") String authId, + @RequestBody WorkoutRequestDto workoutDto){ + TrainingScheduleDto trainingScheduleDto = + userService.addWorkoutToSchedule(UUID.fromString(authId), workoutDto); + + return ResponseEntity.ok().body(trainingScheduleDto); + } + + @GetMapping("/agreements/pending") + public ResponseEntity> getPendingAgreements(@RequestHeader("X-AUTH-ID") String authId){ + List dtos = + userService.getPendingAgreements(UUID.fromString(authId)); + + return ResponseEntity.ok().body(dtos); + } + + @PutMapping("/agreements/{id}/status") + public ResponseEntity setAcceptanceStatusOfPendingAgreement(@RequestHeader("X-AUTH-ID") String authId, + @RequestBody ContractAgreementStatusRequestDto requestDto, + @PathVariable String id){ + CoachClientAgreementResponseDto responseDto = + userService.setAcceptanceStatusOfPendingAgreement(UUID.fromString(authId), UUID.fromString(id), requestDto); + + return ResponseEntity.ok().body(responseDto); } } diff --git a/client-service/src/main/java/com/cm/clientservice/controller/CoachingController.java b/client-service/src/main/java/com/cm/clientservice/controller/CoachingController.java new file mode 100644 index 0000000..a2b0337 --- /dev/null +++ b/client-service/src/main/java/com/cm/clientservice/controller/CoachingController.java @@ -0,0 +1,110 @@ +package com.cm.clientservice.controller; +import com.cm.clientservice.dto.UserResponseDTO; +import com.cm.clientservice.dto.contract.template.AgreementTemplateRequestDto; +import com.cm.clientservice.dto.contract.template.AgreementTemplateResponseDto; +import com.cm.clientservice.dto.contract.CoachClientAgreementRequestDto; +import com.cm.clientservice.dto.contract.CoachClientAgreementResponseDto; +import com.cm.clientservice.dto.scheduling.TrainingScheduleDto; +import com.cm.clientservice.dto.scheduling.workout.WorkoutRequestDto; +import com.cm.clientservice.service.UserService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; + +import java.util.List; +import java.util.UUID; + +@RestController +@RequestMapping("clients/coaching") +@Tag(name = "coaching", description = "API for coaching management.") +public class CoachingController { + + private final UserService userService; + + public CoachingController(UserService userService){ + this.userService = userService; + } + + @GetMapping("/clients") + @Operation(summary= "") + public ResponseEntity> getClients(@RequestHeader("X-AUTH-ID") String authId){ + + List clients = userService.getClientsOfCoach(UUID.fromString(authId)); + return ResponseEntity.ok().body(clients); + } + + @GetMapping("/clients/{clientId}/schedule") + @Operation(summary = "Get the clients training schedule for the coach to view. ") + public ResponseEntity getClientSchedule(@RequestHeader("X-AUTH-ID") String authId, + @PathVariable String clientId){ + + TrainingScheduleDto trainingScheduleDto = + userService.getClientTrainingSchedule(UUID.fromString(clientId), UUID.fromString(authId)); + + return ResponseEntity.ok().body(trainingScheduleDto); + } + + @DeleteMapping("/clients/{clientId}/schedule/workout/{id}") + public ResponseEntity deleteWorkoutFromClientSchedule(@RequestHeader("X-AUTH-ID") String authId, + @PathVariable String clientId, + @PathVariable String id){ + + TrainingScheduleDto trainingScheduleDto = + userService.removeWorkoutFromClientSchedule(UUID.fromString(authId), UUID.fromString(clientId), UUID.fromString(id)); + + return ResponseEntity.ok().body(trainingScheduleDto); + } + + @PostMapping("/clients/{clientId}/schedule/workout") + public ResponseEntity addWorkoutToClientSchedule(@RequestHeader("X-AUTH-ID") String authId, + @PathVariable String clientId, + @RequestBody WorkoutRequestDto workoutDto){ + TrainingScheduleDto trainingScheduleDto = + userService.addWorkoutToClientsSchedule(UUID.fromString(authId), UUID.fromString(clientId), workoutDto); + + return ResponseEntity.ok().body(trainingScheduleDto); + } + + + @PostMapping("/agreement/{clientId}") + public ResponseEntity proposeClientAgreement(@RequestHeader("X-AUTH-ID") String coachAuthId, + @PathVariable String clientId, + @RequestBody CoachClientAgreementRequestDto coachClientAgreement){ + CoachClientAgreementResponseDto dto = + userService.proposeCoachClientAgreement(UUID.fromString(coachAuthId), + UUID.fromString(clientId), + coachClientAgreement); + + return ResponseEntity.ok().body(dto); + } + + @PutMapping("/agreement/withdraw/{agreementId}") + public ResponseEntity withdrawCoachesAgreementAcceptance(@RequestHeader("X-AUTH-ID") String coachAuthId, + @PathVariable String agreementId){ + //TODO: Alert user of withdrawal. + //TODO: Alert billing service of end of contract. + + CoachClientAgreementResponseDto dto = + userService.withdrawCoachesAgreementAcceptance(UUID.fromString(coachAuthId), UUID.fromString(agreementId)); + + return ResponseEntity.ok().body(dto); + } + + @PostMapping("/templates") + public ResponseEntity createAgreementTemplate(@RequestHeader("X-AUTH-ID") String coachAuthId, + @RequestBody AgreementTemplateRequestDto templateRequestDto){ + AgreementTemplateResponseDto dto = + userService.createAgreementTemplate(UUID.fromString(coachAuthId), templateRequestDto); + + return ResponseEntity.ok().body(dto); + } + + @GetMapping("/templates") + public ResponseEntity> getCoachesAgreementTemplates(@RequestHeader("X-AUTH-ID") String coachAuthId){ + List dtoList = + userService.getCoachAgreementTemplates(UUID.fromString(coachAuthId)); + + return ResponseEntity.ok().body(dtoList); + } +} diff --git a/client-service/src/main/java/com/cm/clientservice/controller/MovementController.java b/client-service/src/main/java/com/cm/clientservice/controller/MovementController.java new file mode 100644 index 0000000..13cec7f --- /dev/null +++ b/client-service/src/main/java/com/cm/clientservice/controller/MovementController.java @@ -0,0 +1,35 @@ +package com.cm.clientservice.controller; +import com.cm.clientservice.dto.scheduling.movement.MovementCreateRequestDto; +import com.cm.clientservice.dto.scheduling.movement.MovementResponseDto; +import com.cm.clientservice.service.MovementService; +import io.swagger.v3.oas.annotations.tags.Tag; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; +import java.util.UUID; + +@RestController +@RequestMapping("/clients/movements") +@Tag(name="movements", description = "API for management of movements. ") +public class MovementController { + + private final MovementService movementService; + public MovementController(MovementService movementService){ + this.movementService = movementService; + } + + @PostMapping + public ResponseEntity createMovement(@RequestBody MovementCreateRequestDto movementCreateRequestDto){ + MovementResponseDto movementResponseDto = + movementService.createMovement(movementCreateRequestDto); + + return ResponseEntity.ok().body(movementResponseDto); + } + + @GetMapping("/{movementId}") + public ResponseEntity getMovementById(@PathVariable String movementId){ + MovementResponseDto movementResponseDto = + movementService.getMovementById(UUID.fromString(movementId)); + + return ResponseEntity.ok().body(movementResponseDto); + } +} diff --git a/client-service/src/main/java/com/cm/clientservice/controller/UserController.java b/client-service/src/main/java/com/cm/clientservice/controller/UserController.java index 60fb3d2..f11ee9b 100644 --- a/client-service/src/main/java/com/cm/clientservice/controller/UserController.java +++ b/client-service/src/main/java/com/cm/clientservice/controller/UserController.java @@ -1,5 +1,4 @@ package com.cm.clientservice.controller; - import com.cm.clientservice.dto.validators.CreateUserValidationGroup; import com.cm.clientservice.service.UserService; import jakarta.validation.groups.Default; @@ -17,8 +16,8 @@ import java.util.UUID; @RestController -@RequestMapping("/clients") -@Tag(name="Clients", description = "API for managing Clients") +@RequestMapping("clients/users") +@Tag(name="Users", description = "API for managing Users") public class UserController { private final Logger log = LoggerFactory.getLogger(UserController.class); @@ -28,6 +27,7 @@ public UserController(UserService userService){ this.userService = userService; } + @PostMapping @Operation(summary = "Create a user.") public ResponseEntity createUser( @@ -35,7 +35,6 @@ public ResponseEntity createUser( @RequestBody UserRequestDTO userRequestDTO, @RequestHeader("X-AUTH-ID") String auth_id ){ - log.debug("AUTH ID IS {}", auth_id); UserResponseDTO userResponseDTO = userService.createUser(userRequestDTO, auth_id); return ResponseEntity.ok().body(userResponseDTO); } @@ -48,6 +47,7 @@ public ResponseEntity> getAllUsers(){ return ResponseEntity.ok().body(users); } + @PutMapping("/") @Operation(summary = "Update a user") public ResponseEntity updateUser(@RequestHeader("X-AUTH-ID") String id, @@ -58,17 +58,9 @@ public ResponseEntity updateUser(@RequestHeader("X-AUTH-ID") St String token = authHeader.substring(tokenStartIdx); UUID authId = UUID.fromString(id); - userService.updateUser(authId, userRequestDTO, token); + UserResponseDTO updatedUser = userService.updateUser(authId, userRequestDTO, token); - return ResponseEntity.ok().body(new UserResponseDTO()); - } - - - @GetMapping("/user-clients/{id}") - @Operation(summary = "Get all the clients of a user.") - public ResponseEntity> getAllUsersClients(@PathVariable UUID id){ - //TODO: Implement me and replace tis - return ResponseEntity.ok().body(List.of(new UserResponseDTO())); + return ResponseEntity.ok().body(updatedUser); } @@ -78,5 +70,4 @@ public ResponseEntity deleteUser(@PathVariable UUID id){ userService.deleteUser(id); return ResponseEntity.noContent().build(); } - } diff --git a/client-service/src/main/java/com/cm/clientservice/dto/contract/CoachClientAgreementRequestDto.java b/client-service/src/main/java/com/cm/clientservice/dto/contract/CoachClientAgreementRequestDto.java new file mode 100644 index 0000000..061cc3a --- /dev/null +++ b/client-service/src/main/java/com/cm/clientservice/dto/contract/CoachClientAgreementRequestDto.java @@ -0,0 +1,14 @@ +package com.cm.clientservice.dto.contract; +import com.cm.clientservice.dto.contract.template.AgreementTemplateReuseRequestDto; +import jakarta.validation.constraints.NotNull; +import lombok.Getter; +import lombok.Setter; + +@Getter +@Setter +public class CoachClientAgreementRequestDto { + @NotNull + private String startDate; + @NotNull + private AgreementTemplateReuseRequestDto template; +} diff --git a/client-service/src/main/java/com/cm/clientservice/dto/contract/CoachClientAgreementResponseDto.java b/client-service/src/main/java/com/cm/clientservice/dto/contract/CoachClientAgreementResponseDto.java new file mode 100644 index 0000000..2eb206b --- /dev/null +++ b/client-service/src/main/java/com/cm/clientservice/dto/contract/CoachClientAgreementResponseDto.java @@ -0,0 +1,20 @@ +package com.cm.clientservice.dto.contract; + +import com.cm.clientservice.dto.contract.template.AgreementTemplateResponseDto; +import lombok.Getter; +import lombok.Setter; + +@Getter +@Setter +public class CoachClientAgreementResponseDto { + private String id; + + private String clientId; + private String coachId; + + private String startDate; + private AgreementTemplateResponseDto agreementTemplateResponseDto; + + private String clientIsInAgreement; + private String coachIsInAgreement; +} diff --git a/client-service/src/main/java/com/cm/clientservice/dto/contract/ContractAgreementStatusRequestDto.java b/client-service/src/main/java/com/cm/clientservice/dto/contract/ContractAgreementStatusRequestDto.java new file mode 100644 index 0000000..af1c0ec --- /dev/null +++ b/client-service/src/main/java/com/cm/clientservice/dto/contract/ContractAgreementStatusRequestDto.java @@ -0,0 +1,12 @@ +package com.cm.clientservice.dto.contract; + +import jakarta.validation.constraints.NotBlank; +import lombok.Getter; +import lombok.Setter; + +@Getter +@Setter +public class ContractAgreementStatusRequestDto { + @NotBlank + private Boolean userIsInAgreement; +} diff --git a/client-service/src/main/java/com/cm/clientservice/dto/contract/template/AgreementTemplateRequestDto.java b/client-service/src/main/java/com/cm/clientservice/dto/contract/template/AgreementTemplateRequestDto.java new file mode 100644 index 0000000..e63fa14 --- /dev/null +++ b/client-service/src/main/java/com/cm/clientservice/dto/contract/template/AgreementTemplateRequestDto.java @@ -0,0 +1,30 @@ +package com.cm.clientservice.dto.contract.template; + +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; +import jakarta.validation.constraints.Positive; +import jakarta.validation.constraints.PositiveOrZero; +import lombok.Getter; +import lombok.Setter; + +@Getter +@Setter +public class AgreementTemplateRequestDto { + + @NotBlank + private String templateName; + + @NotNull + @PositiveOrZero + private String paymentAmount; + + @NotNull + @Positive + private String daysBetweenPayments; + + @NotBlank + private String termsAndConditions; + + @NotBlank + private String allowPublicTemplateReuse; +} diff --git a/client-service/src/main/java/com/cm/clientservice/dto/contract/template/AgreementTemplateResponseDto.java b/client-service/src/main/java/com/cm/clientservice/dto/contract/template/AgreementTemplateResponseDto.java new file mode 100644 index 0000000..e73c415 --- /dev/null +++ b/client-service/src/main/java/com/cm/clientservice/dto/contract/template/AgreementTemplateResponseDto.java @@ -0,0 +1,16 @@ +package com.cm.clientservice.dto.contract.template; + +import lombok.Getter; +import lombok.Setter; + +@Getter +@Setter +public class AgreementTemplateResponseDto { + private String id; + private String templateName; + private String authorId; + private String paymentAmount; + private String daysBetweenPayments; + private String termsAndConditions; + private String version; +} diff --git a/client-service/src/main/java/com/cm/clientservice/dto/contract/template/AgreementTemplateReuseRequestDto.java b/client-service/src/main/java/com/cm/clientservice/dto/contract/template/AgreementTemplateReuseRequestDto.java new file mode 100644 index 0000000..2347b24 --- /dev/null +++ b/client-service/src/main/java/com/cm/clientservice/dto/contract/template/AgreementTemplateReuseRequestDto.java @@ -0,0 +1,10 @@ +package com.cm.clientservice.dto.contract.template; + +import lombok.Getter; +import lombok.Setter; + +@Getter +@Setter +public class AgreementTemplateReuseRequestDto { + private String id; +} diff --git a/client-service/src/main/java/com/cm/clientservice/dto/scheduling/TrainingScheduleDto.java b/client-service/src/main/java/com/cm/clientservice/dto/scheduling/TrainingScheduleDto.java new file mode 100644 index 0000000..4fc1b33 --- /dev/null +++ b/client-service/src/main/java/com/cm/clientservice/dto/scheduling/TrainingScheduleDto.java @@ -0,0 +1,15 @@ +package com.cm.clientservice.dto.scheduling; + +import com.cm.clientservice.dto.scheduling.workout.WorkoutResponseDto; +import lombok.Getter; +import lombok.Setter; + +import java.util.List; + +@Getter +@Setter +public class TrainingScheduleDto { + + private String id; + private List workouts; +} diff --git a/client-service/src/main/java/com/cm/clientservice/dto/scheduling/exercise/ExerciseRequestDto.java b/client-service/src/main/java/com/cm/clientservice/dto/scheduling/exercise/ExerciseRequestDto.java new file mode 100644 index 0000000..3c69ff9 --- /dev/null +++ b/client-service/src/main/java/com/cm/clientservice/dto/scheduling/exercise/ExerciseRequestDto.java @@ -0,0 +1,19 @@ +package com.cm.clientservice.dto.scheduling.exercise; +import com.cm.clientservice.dto.scheduling.movement.MovementReuseRequestDto; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; +import lombok.Getter; +import lombok.Setter; + +@Getter +@Setter +public class ExerciseRequestDto { + @NotNull + private MovementReuseRequestDto movement; + @NotBlank + private String numSets; + @NotBlank + private String numReps; + @NotNull + private String coachNotes; +} \ No newline at end of file diff --git a/client-service/src/main/java/com/cm/clientservice/dto/scheduling/exercise/ExerciseResponseDto.java b/client-service/src/main/java/com/cm/clientservice/dto/scheduling/exercise/ExerciseResponseDto.java new file mode 100644 index 0000000..d2c6aa8 --- /dev/null +++ b/client-service/src/main/java/com/cm/clientservice/dto/scheduling/exercise/ExerciseResponseDto.java @@ -0,0 +1,15 @@ +package com.cm.clientservice.dto.scheduling.exercise; + +import com.cm.clientservice.dto.scheduling.movement.MovementResponseDto; +import lombok.Getter; +import lombok.Setter; + +@Getter +@Setter +public class ExerciseResponseDto { + private String id; + private MovementResponseDto movement; + private String numSets; + private String numReps; + private String coachNotes; +} diff --git a/client-service/src/main/java/com/cm/clientservice/dto/scheduling/movement/MovementCreateRequestDto.java b/client-service/src/main/java/com/cm/clientservice/dto/scheduling/movement/MovementCreateRequestDto.java new file mode 100644 index 0000000..6fc5600 --- /dev/null +++ b/client-service/src/main/java/com/cm/clientservice/dto/scheduling/movement/MovementCreateRequestDto.java @@ -0,0 +1,14 @@ +package com.cm.clientservice.dto.scheduling.movement; + +import jakarta.validation.constraints.NotNull; +import lombok.Getter; +import lombok.Setter; + +@Getter +@Setter +public class MovementCreateRequestDto { + @NotNull + private String name; + @NotNull + private String description; +} diff --git a/client-service/src/main/java/com/cm/clientservice/dto/scheduling/movement/MovementResponseDto.java b/client-service/src/main/java/com/cm/clientservice/dto/scheduling/movement/MovementResponseDto.java new file mode 100644 index 0000000..63cfb6f --- /dev/null +++ b/client-service/src/main/java/com/cm/clientservice/dto/scheduling/movement/MovementResponseDto.java @@ -0,0 +1,14 @@ +package com.cm.clientservice.dto.scheduling.movement; + +import lombok.Getter; +import lombok.Setter; + +@Getter +@Setter +public class MovementResponseDto { + private String id; + private String name; + private String description; +} + + diff --git a/client-service/src/main/java/com/cm/clientservice/dto/scheduling/movement/MovementReuseRequestDto.java b/client-service/src/main/java/com/cm/clientservice/dto/scheduling/movement/MovementReuseRequestDto.java new file mode 100644 index 0000000..55fa688 --- /dev/null +++ b/client-service/src/main/java/com/cm/clientservice/dto/scheduling/movement/MovementReuseRequestDto.java @@ -0,0 +1,10 @@ +package com.cm.clientservice.dto.scheduling.movement; + +import lombok.Getter; +import lombok.Setter; + +@Getter +@Setter +public final class MovementReuseRequestDto{ + private String id; +} diff --git a/client-service/src/main/java/com/cm/clientservice/dto/scheduling/workout/WorkoutRequestDto.java b/client-service/src/main/java/com/cm/clientservice/dto/scheduling/workout/WorkoutRequestDto.java new file mode 100644 index 0000000..f758888 --- /dev/null +++ b/client-service/src/main/java/com/cm/clientservice/dto/scheduling/workout/WorkoutRequestDto.java @@ -0,0 +1,25 @@ +package com.cm.clientservice.dto.scheduling.workout; +import com.cm.clientservice.dto.scheduling.exercise.ExerciseRequestDto; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; +import lombok.Getter; +import lombok.Setter; +import java.util.List; + +@Getter +@Setter +public class WorkoutRequestDto { + @NotNull(message = "Exercises should not be null.") + private List exercises; + + private String date; + + @NotNull(message = "Workout notes should not be null.") + private String workoutNotes; + + @NotBlank(message = "Completion flag should not be blank.") + private String isCompleted; + + @NotNull + private String trainingScheduleId; +} diff --git a/client-service/src/main/java/com/cm/clientservice/dto/scheduling/workout/WorkoutResponseDto.java b/client-service/src/main/java/com/cm/clientservice/dto/scheduling/workout/WorkoutResponseDto.java new file mode 100644 index 0000000..b271d25 --- /dev/null +++ b/client-service/src/main/java/com/cm/clientservice/dto/scheduling/workout/WorkoutResponseDto.java @@ -0,0 +1,17 @@ +package com.cm.clientservice.dto.scheduling.workout; + +import com.cm.clientservice.dto.scheduling.exercise.ExerciseResponseDto; +import lombok.Getter; +import lombok.Setter; + +import java.util.List; + +@Getter +@Setter +public class WorkoutResponseDto { + private String id; + private List exercises; + private String date; + private String workoutNotes; + private String isCompleted; +} diff --git a/client-service/src/main/java/com/cm/clientservice/exception/GlobalExceptionHandler.java b/client-service/src/main/java/com/cm/clientservice/exception/GlobalExceptionHandler.java index 4459c3a..e8b54a6 100644 --- a/client-service/src/main/java/com/cm/clientservice/exception/GlobalExceptionHandler.java +++ b/client-service/src/main/java/com/cm/clientservice/exception/GlobalExceptionHandler.java @@ -1,4 +1,6 @@ package com.cm.clientservice.exception; +import com.cm.clientservice.exception.users.EmailAlreadyExistsException; +import com.cm.clientservice.exception.users.UserNotFoundException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.http.ResponseEntity; diff --git a/client-service/src/main/java/com/cm/clientservice/exception/contract/AgreementTemplateNotFoundException.java b/client-service/src/main/java/com/cm/clientservice/exception/contract/AgreementTemplateNotFoundException.java new file mode 100644 index 0000000..402cccb --- /dev/null +++ b/client-service/src/main/java/com/cm/clientservice/exception/contract/AgreementTemplateNotFoundException.java @@ -0,0 +1,7 @@ +package com.cm.clientservice.exception.contract; + +public class AgreementTemplateNotFoundException extends RuntimeException{ + public AgreementTemplateNotFoundException(String message) { + super(message); + } +} diff --git a/client-service/src/main/java/com/cm/clientservice/exception/contract/CoachClientAgreementNotFoundException.java b/client-service/src/main/java/com/cm/clientservice/exception/contract/CoachClientAgreementNotFoundException.java new file mode 100644 index 0000000..0e3d2bf --- /dev/null +++ b/client-service/src/main/java/com/cm/clientservice/exception/contract/CoachClientAgreementNotFoundException.java @@ -0,0 +1,7 @@ +package com.cm.clientservice.exception.contract; + +public class CoachClientAgreementNotFoundException extends RuntimeException{ + public CoachClientAgreementNotFoundException(String message){ + super(message); + } +} diff --git a/client-service/src/main/java/com/cm/clientservice/exception/schedule/MovementNotFoundException.java b/client-service/src/main/java/com/cm/clientservice/exception/schedule/MovementNotFoundException.java new file mode 100644 index 0000000..d0da5dd --- /dev/null +++ b/client-service/src/main/java/com/cm/clientservice/exception/schedule/MovementNotFoundException.java @@ -0,0 +1,7 @@ +package com.cm.clientservice.exception.schedule; + +public class MovementNotFoundException extends RuntimeException { + public MovementNotFoundException(String message){ + super(message); + } +} diff --git a/client-service/src/main/java/com/cm/clientservice/exception/schedule/TrainingScheduleNotFoundException.java b/client-service/src/main/java/com/cm/clientservice/exception/schedule/TrainingScheduleNotFoundException.java new file mode 100644 index 0000000..b8b2520 --- /dev/null +++ b/client-service/src/main/java/com/cm/clientservice/exception/schedule/TrainingScheduleNotFoundException.java @@ -0,0 +1,7 @@ +package com.cm.clientservice.exception.schedule; + +public class TrainingScheduleNotFoundException extends RuntimeException{ + public TrainingScheduleNotFoundException(String message){ + super(message); + } +} diff --git a/client-service/src/main/java/com/cm/clientservice/exception/schedule/UnauthorizedScheduleAccessException.java b/client-service/src/main/java/com/cm/clientservice/exception/schedule/UnauthorizedScheduleAccessException.java new file mode 100644 index 0000000..3358115 --- /dev/null +++ b/client-service/src/main/java/com/cm/clientservice/exception/schedule/UnauthorizedScheduleAccessException.java @@ -0,0 +1,7 @@ +package com.cm.clientservice.exception.schedule; + +public class UnauthorizedScheduleAccessException extends RuntimeException{ + public UnauthorizedScheduleAccessException(String message){ + super(message); + } +} diff --git a/client-service/src/main/java/com/cm/clientservice/exception/schedule/WorkoutNotFoundException.java b/client-service/src/main/java/com/cm/clientservice/exception/schedule/WorkoutNotFoundException.java new file mode 100644 index 0000000..be2ac6f --- /dev/null +++ b/client-service/src/main/java/com/cm/clientservice/exception/schedule/WorkoutNotFoundException.java @@ -0,0 +1,7 @@ +package com.cm.clientservice.exception.schedule; + +public class WorkoutNotFoundException extends RuntimeException{ + public WorkoutNotFoundException(String message){ + super(message); + } +} diff --git a/client-service/src/main/java/com/cm/clientservice/exception/EmailAlreadyExistsException.java b/client-service/src/main/java/com/cm/clientservice/exception/users/EmailAlreadyExistsException.java similarity index 75% rename from client-service/src/main/java/com/cm/clientservice/exception/EmailAlreadyExistsException.java rename to client-service/src/main/java/com/cm/clientservice/exception/users/EmailAlreadyExistsException.java index 5466152..45d0ba4 100644 --- a/client-service/src/main/java/com/cm/clientservice/exception/EmailAlreadyExistsException.java +++ b/client-service/src/main/java/com/cm/clientservice/exception/users/EmailAlreadyExistsException.java @@ -1,4 +1,4 @@ -package com.cm.clientservice.exception; +package com.cm.clientservice.exception.users; public class EmailAlreadyExistsException extends RuntimeException{ public EmailAlreadyExistsException(String message) {super(message);} diff --git a/client-service/src/main/java/com/cm/clientservice/exception/UserNotFoundException.java b/client-service/src/main/java/com/cm/clientservice/exception/users/UserNotFoundException.java similarity index 75% rename from client-service/src/main/java/com/cm/clientservice/exception/UserNotFoundException.java rename to client-service/src/main/java/com/cm/clientservice/exception/users/UserNotFoundException.java index b02db8b..af5250c 100644 --- a/client-service/src/main/java/com/cm/clientservice/exception/UserNotFoundException.java +++ b/client-service/src/main/java/com/cm/clientservice/exception/users/UserNotFoundException.java @@ -1,4 +1,4 @@ -package com.cm.clientservice.exception; +package com.cm.clientservice.exception.users; public class UserNotFoundException extends RuntimeException{ public UserNotFoundException(String message){ diff --git a/client-service/src/main/java/com/cm/clientservice/mapper/contract/AgreementTemplateMapper.java b/client-service/src/main/java/com/cm/clientservice/mapper/contract/AgreementTemplateMapper.java new file mode 100644 index 0000000..df8f206 --- /dev/null +++ b/client-service/src/main/java/com/cm/clientservice/mapper/contract/AgreementTemplateMapper.java @@ -0,0 +1,43 @@ +package com.cm.clientservice.mapper.contract; + +import com.cm.clientservice.dto.contract.template.AgreementTemplateRequestDto; +import com.cm.clientservice.dto.contract.template.AgreementTemplateResponseDto; +import com.cm.clientservice.model.contract.AgreementTemplate; + +import java.util.List; + +public class AgreementTemplateMapper { + + public static AgreementTemplateResponseDto toDto(AgreementTemplate agreementTemplate){ + AgreementTemplateResponseDto dto = new AgreementTemplateResponseDto(); + + dto.setId(agreementTemplate.getId().toString()); + dto.setVersion(agreementTemplate.getVersion().toString()); + dto.setPaymentAmount(agreementTemplate.getPaymentAmount().toString()); + dto.setTermsAndConditions(agreementTemplate.getTermsAndConditions()); + dto.setDaysBetweenPayments(agreementTemplate.getDaysBetweenPayments().toString()); + dto.setAuthorId(agreementTemplate.getAuthor().getId().toString()); + dto.setTemplateName(agreementTemplate.getTemplateName()); + + return dto; + } + + public static List toDto(List agreementTemplates){ + return agreementTemplates + .stream() + .map(AgreementTemplateMapper::toDto) + .toList(); + } + + public static AgreementTemplate toModel(AgreementTemplateRequestDto dto) { + AgreementTemplate template = new AgreementTemplate(); + + template.setPaymentAmount(Double.parseDouble(dto.getPaymentAmount())); + template.setTermsAndConditions(dto.getTermsAndConditions()); + template.setAllowPublicTemplateReuse(Boolean.valueOf(dto.getAllowPublicTemplateReuse())); + template.setDaysBetweenPayments(Integer.parseInt(dto.getDaysBetweenPayments())); + template.setTemplateName(dto.getTemplateName()); + + return template; + } +} diff --git a/client-service/src/main/java/com/cm/clientservice/mapper/contract/CoachClientAgreementMapper.java b/client-service/src/main/java/com/cm/clientservice/mapper/contract/CoachClientAgreementMapper.java new file mode 100644 index 0000000..d121607 --- /dev/null +++ b/client-service/src/main/java/com/cm/clientservice/mapper/contract/CoachClientAgreementMapper.java @@ -0,0 +1,32 @@ +package com.cm.clientservice.mapper.contract; +import com.cm.clientservice.dto.contract.CoachClientAgreementRequestDto; +import com.cm.clientservice.dto.contract.CoachClientAgreementResponseDto; +import com.cm.clientservice.model.contract.CoachClientAgreement; + +import java.time.LocalDate; + +public class CoachClientAgreementMapper { + public static CoachClientAgreementResponseDto toDto(CoachClientAgreement agreement) { + CoachClientAgreementResponseDto dto = new CoachClientAgreementResponseDto(); + + dto.setAgreementTemplateResponseDto(AgreementTemplateMapper.toDto(agreement.getAgreementTemplate())); + dto.setClientId(agreement.getClient().getId().toString()); + dto.setCoachId(agreement.getCoach().getId().toString()); + dto.setStartDate(agreement.getStartDate().toString()); + dto.setId(agreement.getId().toString()); + dto.setClientIsInAgreement(agreement.getClientIsInAgreement().toString()); + dto.setCoachIsInAgreement(agreement.getCoachIsInAgreement().toString()); + + return dto; + } + + public static CoachClientAgreement toModel(CoachClientAgreementRequestDto dto){ + CoachClientAgreement agreement = new CoachClientAgreement(); + + agreement.setClientIsInAgreement(Boolean.FALSE); + agreement.setCoachIsInAgreement(Boolean.TRUE); + agreement.setStartDate(LocalDate.parse(dto.getStartDate())); + + return agreement; + } +} diff --git a/client-service/src/main/java/com/cm/clientservice/mapper/schedule/ExerciseMapper.java b/client-service/src/main/java/com/cm/clientservice/mapper/schedule/ExerciseMapper.java new file mode 100644 index 0000000..663f7e7 --- /dev/null +++ b/client-service/src/main/java/com/cm/clientservice/mapper/schedule/ExerciseMapper.java @@ -0,0 +1,42 @@ +package com.cm.clientservice.mapper.schedule; +import com.cm.clientservice.dto.scheduling.exercise.ExerciseRequestDto; +import com.cm.clientservice.dto.scheduling.exercise.ExerciseResponseDto; +import com.cm.clientservice.model.schedule.Exercise; + +import java.util.List; + +public class ExerciseMapper { + public static List toDto(List exercises) { + return exercises.stream() + .map(ExerciseMapper::toDto) + .toList(); + } + + public static ExerciseResponseDto toDto(Exercise exercise){ + ExerciseResponseDto dto = new ExerciseResponseDto(); + + dto.setId(exercise.getId().toString()); + dto.setMovement(MovementMapper.toDto(exercise.getMovement())); + dto.setCoachNotes(exercise.getCoachNotes()); + dto.setNumReps(String.valueOf(exercise.getNumReps())); + dto.setNumSets(String.valueOf(exercise.getNumSets())); + return dto; + } + + public static List toModel(List exerciseDtos) { + return exerciseDtos.stream() + .map(ExerciseMapper::toModel) + .toList(); + } + + public static Exercise toModel(ExerciseRequestDto exerciseDto){ + Exercise exercise = new Exercise(); + + exercise.setCoachNotes(exerciseDto.getCoachNotes()); + exercise.setMovement(MovementMapper.toModel(exerciseDto.getMovement())); + exercise.setNumSets(Integer.parseInt(exerciseDto.getNumSets())); + exercise.setNumReps(Integer.parseInt(exerciseDto.getNumReps())); + + return exercise; + } +} diff --git a/client-service/src/main/java/com/cm/clientservice/mapper/schedule/MovementMapper.java b/client-service/src/main/java/com/cm/clientservice/mapper/schedule/MovementMapper.java new file mode 100644 index 0000000..0003819 --- /dev/null +++ b/client-service/src/main/java/com/cm/clientservice/mapper/schedule/MovementMapper.java @@ -0,0 +1,23 @@ +package com.cm.clientservice.mapper.schedule; +import com.cm.clientservice.dto.scheduling.movement.MovementResponseDto; +import com.cm.clientservice.dto.scheduling.movement.MovementReuseRequestDto; +import com.cm.clientservice.model.schedule.Movement; + +import java.util.UUID; + +public class MovementMapper { + + public static MovementResponseDto toDto(Movement movement){ + MovementResponseDto dto = new MovementResponseDto(); + dto.setId(movement.getId().toString()); + dto.setName(movement.getName()); + dto.setDescription(movement.getDescription()); + return dto; + } + + public static Movement toModel(MovementReuseRequestDto movementDto) { + Movement movement = new Movement(); + movement.setId(UUID.fromString(movementDto.getId())); + return movement; + } +} diff --git a/client-service/src/main/java/com/cm/clientservice/mapper/schedule/TrainingScheduleMapper.java b/client-service/src/main/java/com/cm/clientservice/mapper/schedule/TrainingScheduleMapper.java new file mode 100644 index 0000000..ac2b3f4 --- /dev/null +++ b/client-service/src/main/java/com/cm/clientservice/mapper/schedule/TrainingScheduleMapper.java @@ -0,0 +1,14 @@ +package com.cm.clientservice.mapper.schedule; +import com.cm.clientservice.dto.scheduling.TrainingScheduleDto; +import com.cm.clientservice.model.schedule.TrainingSchedule; + +public class TrainingScheduleMapper { + + public static TrainingScheduleDto toDto(TrainingSchedule trainingSchedule) { + TrainingScheduleDto dto = new TrainingScheduleDto(); + + dto.setId(String.valueOf(trainingSchedule.getId())); + dto.setWorkouts(WorkoutMapper.toDto(trainingSchedule.getWorkouts())); + return dto; + } +} \ No newline at end of file diff --git a/client-service/src/main/java/com/cm/clientservice/mapper/schedule/WorkoutMapper.java b/client-service/src/main/java/com/cm/clientservice/mapper/schedule/WorkoutMapper.java new file mode 100644 index 0000000..6420207 --- /dev/null +++ b/client-service/src/main/java/com/cm/clientservice/mapper/schedule/WorkoutMapper.java @@ -0,0 +1,45 @@ +package com.cm.clientservice.mapper.schedule; + +import com.cm.clientservice.dto.scheduling.workout.WorkoutRequestDto; +import com.cm.clientservice.dto.scheduling.workout.WorkoutResponseDto; +import com.cm.clientservice.model.schedule.Exercise; +import com.cm.clientservice.model.schedule.Workout; + +import java.time.LocalDate; +import java.util.List; + +public class WorkoutMapper { + + public static List toDto(List workouts){ + return workouts + .stream() + .map(WorkoutMapper::toDto) + .toList(); + } + + public static WorkoutResponseDto toDto(Workout workout){ + WorkoutResponseDto dto = new WorkoutResponseDto(); + + dto.setId(String.valueOf(workout.getId())); + dto.setDate(workout.getDate().toString()); + dto.setWorkoutNotes(workout.getWorkoutNotes()); + dto.setIsCompleted(workout.getIsCompleted().toString()); + dto.setExercises(ExerciseMapper.toDto(workout.getExercises())); + + return dto; + } + + public static Workout toModel(WorkoutRequestDto workoutRequestDto){ + Workout workout = new Workout(); + + workout.setIsCompleted(Boolean.valueOf(workoutRequestDto.getIsCompleted())); + workout.setDate(LocalDate.parse(workoutRequestDto.getDate())); + workout.setWorkoutNotes(workoutRequestDto.getWorkoutNotes()); + + List exercises = ExerciseMapper.toModel(workoutRequestDto.getExercises()); + for (Exercise e : exercises) e.setWorkout(workout); + workout.setExercises(exercises); + + return workout; + } +} diff --git a/client-service/src/main/java/com/cm/clientservice/model/ClientProfile.java b/client-service/src/main/java/com/cm/clientservice/model/ClientProfile.java deleted file mode 100644 index 7a75c6c..0000000 --- a/client-service/src/main/java/com/cm/clientservice/model/ClientProfile.java +++ /dev/null @@ -1,27 +0,0 @@ -package com.cm.clientservice.model; - -import jakarta.persistence.*; -import jakarta.validation.constraints.NotNull; -import lombok.Getter; -import lombok.Setter; - -import java.util.List; -import java.util.UUID; - -@Entity -@Getter -@Setter -public class ClientProfile { - - @Id - @GeneratedValue - private UUID id; - - @ManyToMany - @JoinTable( - name = "client_coach_map", - joinColumns = @JoinColumn(name = "client_profile_id"), - inverseJoinColumns = @JoinColumn(name = "coach_profile_id") - ) - private List coaches; -} diff --git a/client-service/src/main/java/com/cm/clientservice/model/CoachProfile.java b/client-service/src/main/java/com/cm/clientservice/model/CoachProfile.java deleted file mode 100644 index df64424..0000000 --- a/client-service/src/main/java/com/cm/clientservice/model/CoachProfile.java +++ /dev/null @@ -1,24 +0,0 @@ -package com.cm.clientservice.model; -import jakarta.persistence.*; -import jakarta.validation.constraints.NotNull; -import lombok.Getter; -import lombok.Setter; - -import java.util.List; -import java.util.UUID; - -@Entity -@Getter -@Setter -public class CoachProfile { - - @Id - @GeneratedValue - private UUID id; - - @ManyToMany(mappedBy = "coaches") - private List clients; - - @NotNull - private String biography; -} \ No newline at end of file diff --git a/client-service/src/main/java/com/cm/clientservice/model/User.java b/client-service/src/main/java/com/cm/clientservice/model/User.java index 1b552df..eaf7acc 100644 --- a/client-service/src/main/java/com/cm/clientservice/model/User.java +++ b/client-service/src/main/java/com/cm/clientservice/model/User.java @@ -1,5 +1,8 @@ package com.cm.clientservice.model; +import com.cm.clientservice.model.contract.AgreementTemplate; +import com.cm.clientservice.model.contract.CoachClientAgreement; +import com.cm.clientservice.model.schedule.TrainingSchedule; import jakarta.persistence.*; import jakarta.validation.constraints.Email; import jakarta.validation.constraints.NotBlank; @@ -44,11 +47,16 @@ public class User { @NotBlank private String address; - @OneToOne - @JoinColumn(name = "coach_profile_id") - private CoachProfile coachProfile; + @OneToMany(mappedBy = "coach") + private List agreementsAsCoach; - @OneToOne - @JoinColumn(name = "client_profile_id") - private ClientProfile clientProfile; + @OneToMany(mappedBy = "client") + private List agreementsAsClient; + + @OneToMany(mappedBy = "author") + private List authoredTemplates; + + @OneToOne(cascade = CascadeType.ALL) + @JoinColumn(name="training_schedule_id") + private TrainingSchedule trainingSchedule; } diff --git a/client-service/src/main/java/com/cm/clientservice/model/contract/AgreementTemplate.java b/client-service/src/main/java/com/cm/clientservice/model/contract/AgreementTemplate.java index e49e1aa..5a0fb80 100644 --- a/client-service/src/main/java/com/cm/clientservice/model/contract/AgreementTemplate.java +++ b/client-service/src/main/java/com/cm/clientservice/model/contract/AgreementTemplate.java @@ -1,6 +1,6 @@ package com.cm.clientservice.model.contract; -import com.cm.clientservice.model.CoachProfile; +import com.cm.clientservice.model.User; import jakarta.persistence.*; import jakarta.validation.constraints.NotBlank; import jakarta.validation.constraints.NotNull; @@ -19,10 +19,13 @@ public class AgreementTemplate { @GeneratedValue private UUID id; + @NotBlank(message = "Template name must be provided.") + private String templateName; + @NotNull(message = "Original author must be included for agreement template.") @ManyToOne @JoinColumn(name = "coach_profile_id") - private CoachProfile author; + private User author; @NotNull(message = "Need to specify if author will allow public template reuse.") private Boolean allowPublicTemplateReuse; diff --git a/client-service/src/main/java/com/cm/clientservice/model/contract/CoachClientAgreement.java b/client-service/src/main/java/com/cm/clientservice/model/contract/CoachClientAgreement.java index ab67f6d..131bb76 100644 --- a/client-service/src/main/java/com/cm/clientservice/model/contract/CoachClientAgreement.java +++ b/client-service/src/main/java/com/cm/clientservice/model/contract/CoachClientAgreement.java @@ -1,7 +1,6 @@ package com.cm.clientservice.model.contract; -import com.cm.clientservice.model.ClientProfile; -import com.cm.clientservice.model.CoachProfile; +import com.cm.clientservice.model.User; import jakarta.persistence.*; import jakarta.validation.constraints.NotNull; import lombok.Getter; @@ -14,23 +13,20 @@ @Getter @Setter public class CoachClientAgreement { - /** - Represents a running agreement document between - */ @Id @GeneratedValue private UUID id; @ManyToOne - @JoinColumn(name = "client_profile_id") + @JoinColumn(name = "client_id") @NotNull(message = "Client profile must be included in coach-client agreement. ") - private ClientProfile clientProfile; + private User client; @ManyToOne - @JoinColumn(name = "coach_profile_id") + @JoinColumn(name = "coach_id") @NotNull(message = "Coach profile must be included in coach-client agreement. ") - private CoachProfile coachProfile; + private User coach; @ManyToOne @JoinColumn(name = "agreement_template_id") @@ -39,5 +35,11 @@ public class CoachClientAgreement { @NotNull(message = "Coach-Client Agreement must have a start date.") private LocalDate startDate; + + @NotNull(message = "Client agreement status must be specified. ") + private Boolean clientIsInAgreement; + + @NotNull(message = "Coach agreement status must be specified. ") + private Boolean coachIsInAgreement; } diff --git a/client-service/src/main/java/com/cm/clientservice/model/schedule/Exercise.java b/client-service/src/main/java/com/cm/clientservice/model/schedule/Exercise.java index 4fe8f97..ab5e13c 100644 --- a/client-service/src/main/java/com/cm/clientservice/model/schedule/Exercise.java +++ b/client-service/src/main/java/com/cm/clientservice/model/schedule/Exercise.java @@ -27,4 +27,8 @@ public class Exercise { @NotNull private String coachNotes; + + @ManyToOne + @JoinColumn(name = "workout_id") + private Workout workout; } diff --git a/client-service/src/main/java/com/cm/clientservice/model/schedule/Movement.java b/client-service/src/main/java/com/cm/clientservice/model/schedule/Movement.java index 3df1d65..b4a2fd9 100644 --- a/client-service/src/main/java/com/cm/clientservice/model/schedule/Movement.java +++ b/client-service/src/main/java/com/cm/clientservice/model/schedule/Movement.java @@ -1,8 +1,5 @@ package com.cm.clientservice.model.schedule; -import jakarta.persistence.Column; -import jakarta.persistence.Entity; -import jakarta.persistence.GeneratedValue; -import jakarta.persistence.Id; +import jakarta.persistence.*; import jakarta.validation.constraints.NotBlank; import jakarta.validation.constraints.NotNull; import lombok.Getter; diff --git a/client-service/src/main/java/com/cm/clientservice/model/schedule/TrainingSchedule.java b/client-service/src/main/java/com/cm/clientservice/model/schedule/TrainingSchedule.java new file mode 100644 index 0000000..95de79f --- /dev/null +++ b/client-service/src/main/java/com/cm/clientservice/model/schedule/TrainingSchedule.java @@ -0,0 +1,25 @@ +package com.cm.clientservice.model.schedule; +import com.cm.clientservice.model.User; +import jakarta.persistence.*; +import lombok.Getter; +import lombok.Setter; + +import java.util.ArrayList; +import java.util.List; +import java.util.UUID; + +@Entity +@Getter +@Setter +public class TrainingSchedule { + + @Id + @GeneratedValue + private UUID id; + + @OneToOne + private User user; + + @OneToMany(mappedBy = "trainingSchedule", cascade = CascadeType.ALL, orphanRemoval = true) + private List workouts = new ArrayList<>(); +} diff --git a/client-service/src/main/java/com/cm/clientservice/model/schedule/Workout.java b/client-service/src/main/java/com/cm/clientservice/model/schedule/Workout.java index e437f1e..96aafb7 100644 --- a/client-service/src/main/java/com/cm/clientservice/model/schedule/Workout.java +++ b/client-service/src/main/java/com/cm/clientservice/model/schedule/Workout.java @@ -1,11 +1,12 @@ package com.cm.clientservice.model.schedule; import jakarta.persistence.*; -import jakarta.validation.constraints.NotBlank; import jakarta.validation.constraints.NotNull; +import jakarta.validation.constraints.PositiveOrZero; import lombok.Getter; import lombok.Setter; import java.time.LocalDate; +import java.util.ArrayList; import java.util.List; import java.util.UUID; @@ -17,13 +18,8 @@ public class Workout { @GeneratedValue private UUID id; - @ManyToMany - @JoinTable( - name = "workout_exercise", - joinColumns = @JoinColumn(name = "workout_id"), - inverseJoinColumns = @JoinColumn(name = "exercise_id") - ) - private List exercises; + @OneToMany(mappedBy = "workout", cascade = CascadeType.ALL, orphanRemoval = true) + private List exercises = new ArrayList<>(); @NotNull private LocalDate date; @@ -32,5 +28,9 @@ public class Workout { private String workoutNotes; @NotNull - private boolean isCompleted; + private Boolean isCompleted; + + @ManyToOne + @JoinColumn(name = "training_schedule_id") + private TrainingSchedule trainingSchedule; } diff --git a/client-service/src/main/java/com/cm/clientservice/model/schedule/WorkoutSchedule.java b/client-service/src/main/java/com/cm/clientservice/model/schedule/WorkoutSchedule.java deleted file mode 100644 index 3718998..0000000 --- a/client-service/src/main/java/com/cm/clientservice/model/schedule/WorkoutSchedule.java +++ /dev/null @@ -1,23 +0,0 @@ -package com.cm.clientservice.model.schedule; -import jakarta.persistence.*; -import lombok.Getter; -import lombok.Setter; - -import java.util.List; -import java.util.UUID; - -@Entity -@Getter -@Setter -public class WorkoutSchedule { - - @Id - @GeneratedValue - private UUID id; - - @ManyToMany - @JoinTable(name = "trainingschedule_workout", - joinColumns = @JoinColumn(name = "trainingschedule_id"), - inverseJoinColumns = @JoinColumn(name = "workout_id")) - private List workouts; -} diff --git a/client-service/src/main/java/com/cm/clientservice/repository/AgreementTemplateRepository.java b/client-service/src/main/java/com/cm/clientservice/repository/AgreementTemplateRepository.java new file mode 100644 index 0000000..4189568 --- /dev/null +++ b/client-service/src/main/java/com/cm/clientservice/repository/AgreementTemplateRepository.java @@ -0,0 +1,12 @@ +package com.cm.clientservice.repository; + +import com.cm.clientservice.model.contract.AgreementTemplate; +import org.springframework.data.jpa.repository.JpaRepository; +import java.util.List; + +import java.util.UUID; + +public interface AgreementTemplateRepository extends JpaRepository { + + List findAllByAuthor_Id(UUID authorId); +} diff --git a/client-service/src/main/java/com/cm/clientservice/repository/CoachClientAgreementRepository.java b/client-service/src/main/java/com/cm/clientservice/repository/CoachClientAgreementRepository.java new file mode 100644 index 0000000..a8d1c80 --- /dev/null +++ b/client-service/src/main/java/com/cm/clientservice/repository/CoachClientAgreementRepository.java @@ -0,0 +1,12 @@ +package com.cm.clientservice.repository; + +import com.cm.clientservice.model.contract.CoachClientAgreement; +import org.springframework.data.jpa.repository.JpaRepository; + +import java.util.List; +import java.util.UUID; + +public interface CoachClientAgreementRepository extends JpaRepository { + List findAllByCoach_Id(UUID id); + boolean existsCoachClientAgreementByClient_IdAndCoach_Id(UUID client_id, UUID coach_id); +} diff --git a/client-service/src/main/java/com/cm/clientservice/repository/MovementRepository.java b/client-service/src/main/java/com/cm/clientservice/repository/MovementRepository.java new file mode 100644 index 0000000..f8011b1 --- /dev/null +++ b/client-service/src/main/java/com/cm/clientservice/repository/MovementRepository.java @@ -0,0 +1,11 @@ +package com.cm.clientservice.repository; + +import com.cm.clientservice.model.schedule.Movement; +import org.springframework.data.jpa.repository.JpaRepository; + +import java.util.Optional; +import java.util.UUID; + +public interface MovementRepository extends JpaRepository { + Optional findMovementByName(String name); +} diff --git a/client-service/src/main/java/com/cm/clientservice/repository/TrainingScheduleRepository.java b/client-service/src/main/java/com/cm/clientservice/repository/TrainingScheduleRepository.java new file mode 100644 index 0000000..2e94ef7 --- /dev/null +++ b/client-service/src/main/java/com/cm/clientservice/repository/TrainingScheduleRepository.java @@ -0,0 +1,9 @@ +package com.cm.clientservice.repository; + +import com.cm.clientservice.model.schedule.TrainingSchedule; +import org.springframework.data.jpa.repository.JpaRepository; + +import java.util.UUID; + +public interface TrainingScheduleRepository extends JpaRepository { +} diff --git a/client-service/src/main/java/com/cm/clientservice/repository/UserRepository.java b/client-service/src/main/java/com/cm/clientservice/repository/UserRepository.java index 8bdc12b..a69b758 100644 --- a/client-service/src/main/java/com/cm/clientservice/repository/UserRepository.java +++ b/client-service/src/main/java/com/cm/clientservice/repository/UserRepository.java @@ -1,8 +1,6 @@ package com.cm.clientservice.repository; import com.cm.clientservice.model.User; import org.springframework.data.jpa.repository.JpaRepository; - -import java.util.List; import java.util.Optional; import java.util.UUID; @@ -12,7 +10,5 @@ public interface UserRepository extends JpaRepository { boolean existsByEmailAndIdNot(String email, UUID id); - List findByCoachProfileIsNotNull(); - Optional findByAuthId(UUID authId); } diff --git a/client-service/src/main/java/com/cm/clientservice/service/AgreementTemplateService.java b/client-service/src/main/java/com/cm/clientservice/service/AgreementTemplateService.java new file mode 100644 index 0000000..f1efb4d --- /dev/null +++ b/client-service/src/main/java/com/cm/clientservice/service/AgreementTemplateService.java @@ -0,0 +1,48 @@ +package com.cm.clientservice.service; + +import com.cm.clientservice.dto.contract.template.AgreementTemplateRequestDto; +import com.cm.clientservice.dto.contract.template.AgreementTemplateResponseDto; +import com.cm.clientservice.exception.contract.AgreementTemplateNotFoundException; +import com.cm.clientservice.mapper.contract.AgreementTemplateMapper; +import com.cm.clientservice.model.User; +import com.cm.clientservice.model.contract.AgreementTemplate; +import com.cm.clientservice.repository.AgreementTemplateRepository; +import org.springframework.stereotype.Service; + +import java.util.List; +import java.util.UUID; + +@Service +public class AgreementTemplateService { + + private final AgreementTemplateRepository agreementTemplateRepository; + + public AgreementTemplateService(AgreementTemplateRepository agreementTemplateRepository) { + this.agreementTemplateRepository = agreementTemplateRepository; + } + + public AgreementTemplateResponseDto createAgreementTemplate(User author, + AgreementTemplateRequestDto templateRequestDto){ + + AgreementTemplate template = + AgreementTemplateMapper.toModel(templateRequestDto); + + template.setAuthor(author); + template.setVersion(1); + + AgreementTemplate createdAgreement = + agreementTemplateRepository.save(template); + + return AgreementTemplateMapper.toDto(createdAgreement); + } + + public List findAgreementsAuthoredByCoach(User coach) { + List templates = agreementTemplateRepository.findAllByAuthor_Id(coach.getId()); + return AgreementTemplateMapper.toDto(templates); + } + + public AgreementTemplate findTemplateForContractUse(UUID id) { + return agreementTemplateRepository.findById(id).orElseThrow( + () -> new AgreementTemplateNotFoundException("Agreement template not found with id: " + id)); + } +} diff --git a/client-service/src/main/java/com/cm/clientservice/service/CoachClientAgreementService.java b/client-service/src/main/java/com/cm/clientservice/service/CoachClientAgreementService.java new file mode 100644 index 0000000..329a64b --- /dev/null +++ b/client-service/src/main/java/com/cm/clientservice/service/CoachClientAgreementService.java @@ -0,0 +1,73 @@ +package com.cm.clientservice.service; +import com.cm.clientservice.dto.contract.CoachClientAgreementRequestDto; +import com.cm.clientservice.dto.contract.CoachClientAgreementResponseDto; +import com.cm.clientservice.dto.contract.ContractAgreementStatusRequestDto; +import com.cm.clientservice.mapper.contract.CoachClientAgreementMapper; +import com.cm.clientservice.model.User; +import com.cm.clientservice.model.contract.AgreementTemplate; +import com.cm.clientservice.model.contract.CoachClientAgreement; +import com.cm.clientservice.repository.CoachClientAgreementRepository; +import org.springframework.stereotype.Service; + +import java.util.List; +import java.util.UUID; + +@Service +public class CoachClientAgreementService { + + private final CoachClientAgreementRepository coachClientAgreementRepository; + private final AgreementTemplateService agreementTemplateService; + + public CoachClientAgreementService(CoachClientAgreementRepository coachClientAgreementRepository, + AgreementTemplateService agreementTemplateService){ + this.coachClientAgreementRepository = coachClientAgreementRepository; + this.agreementTemplateService = agreementTemplateService; + } + + public List getCoachClientAgreements(UUID coachId){ + + return coachClientAgreementRepository.findAllByCoach_Id(coachId); + } + + public boolean isUserAClientOfCoach(UUID userId, UUID coachId){ + return coachClientAgreementRepository + .existsCoachClientAgreementByClient_IdAndCoach_Id(userId, coachId); + } + + public boolean isAgreementActive(CoachClientAgreement coachClientAgreement) { + return coachClientAgreement.getCoachIsInAgreement() && + coachClientAgreement.getClientIsInAgreement(); + } + + public CoachClientAgreementResponseDto proposeCoachClientAgreement(User coach, User client, CoachClientAgreementRequestDto agreementDto) { + + CoachClientAgreement agreement = CoachClientAgreementMapper.toModel(agreementDto); + agreement.setCoach(coach); + agreement.setClient(client); + + agreement.setAgreementTemplate(agreementTemplateService.findTemplateForContractUse(UUID.fromString(agreementDto.getTemplate().getId()))); + coachClientAgreementRepository.save(agreement); + + return CoachClientAgreementMapper.toDto(agreement); + } + + public CoachClientAgreementResponseDto withdrawCoachesAgreementAcceptance(CoachClientAgreement agreementToChange) { + //TODO: Alter this to mirror the client one. + agreementToChange.setCoachIsInAgreement(Boolean.FALSE); + CoachClientAgreement updatedAgreement = coachClientAgreementRepository.save(agreementToChange); + return CoachClientAgreementMapper.toDto(updatedAgreement); + } + + public Boolean isAgreementPending(CoachClientAgreement agreement){ + return agreement.getClientIsInAgreement().equals(Boolean.FALSE); + } + + public CoachClientAgreementResponseDto setClientAgreementStatus(CoachClientAgreement agreementToChange, + ContractAgreementStatusRequestDto statusUpdateRequest){ + + agreementToChange.setClientIsInAgreement(statusUpdateRequest.getUserIsInAgreement()); + CoachClientAgreement updatedAgreement = coachClientAgreementRepository.save(agreementToChange); + return CoachClientAgreementMapper.toDto(updatedAgreement); + } + +} diff --git a/client-service/src/main/java/com/cm/clientservice/service/MovementService.java b/client-service/src/main/java/com/cm/clientservice/service/MovementService.java new file mode 100644 index 0000000..3779aa8 --- /dev/null +++ b/client-service/src/main/java/com/cm/clientservice/service/MovementService.java @@ -0,0 +1,38 @@ +package com.cm.clientservice.service; + +import com.cm.clientservice.dto.scheduling.movement.MovementCreateRequestDto; +import com.cm.clientservice.dto.scheduling.movement.MovementResponseDto; +import com.cm.clientservice.exception.schedule.MovementNotFoundException; +import com.cm.clientservice.mapper.schedule.MovementMapper; +import com.cm.clientservice.model.schedule.Movement; +import com.cm.clientservice.repository.MovementRepository; +import org.springframework.stereotype.Service; + +import java.util.UUID; + +@Service +public class MovementService { + + private final MovementRepository movementRepository; + + public MovementService(MovementRepository movementRepository){ + this.movementRepository = movementRepository; + } + + public MovementResponseDto createMovement(MovementCreateRequestDto movementCreateRequestDto) { + Movement movement = new Movement(); + movement.setName(movementCreateRequestDto.getName()); + movement.setDescription(movementCreateRequestDto.getDescription()); + + Movement newMovement = movementRepository.save(movement); + + return MovementMapper.toDto(newMovement); + } + + public MovementResponseDto getMovementById(UUID movementId) { + Movement movement = movementRepository.findById(movementId).orElseThrow( + () -> new MovementNotFoundException("Movement not found with id: " + movementId)); + + return MovementMapper.toDto(movement); + } +} diff --git a/client-service/src/main/java/com/cm/clientservice/service/TrainingScheduleService.java b/client-service/src/main/java/com/cm/clientservice/service/TrainingScheduleService.java new file mode 100644 index 0000000..08cf832 --- /dev/null +++ b/client-service/src/main/java/com/cm/clientservice/service/TrainingScheduleService.java @@ -0,0 +1,58 @@ +package com.cm.clientservice.service; + +import com.cm.clientservice.dto.scheduling.TrainingScheduleDto; +import com.cm.clientservice.dto.scheduling.workout.WorkoutRequestDto; +import com.cm.clientservice.exception.schedule.TrainingScheduleNotFoundException; +import com.cm.clientservice.exception.schedule.WorkoutNotFoundException; +import com.cm.clientservice.mapper.schedule.TrainingScheduleMapper; +import com.cm.clientservice.mapper.schedule.WorkoutMapper; +import com.cm.clientservice.model.schedule.TrainingSchedule; +import com.cm.clientservice.model.schedule.Workout; +import com.cm.clientservice.repository.TrainingScheduleRepository; +import jakarta.transaction.Transactional; +import org.springframework.stereotype.Service; + +import java.util.List; +import java.util.UUID; + +@Service +public class TrainingScheduleService { + + private final TrainingScheduleRepository trainingScheduleRepository; + + public TrainingScheduleService(TrainingScheduleRepository trainingScheduleRepository){ + this.trainingScheduleRepository = trainingScheduleRepository; + } + + @Transactional + public TrainingScheduleDto removeWorkoutFromSchedule(TrainingSchedule schedule, UUID workoutId){ + List workouts = schedule.getWorkouts(); + + Workout workoutToRemove = + workouts.stream() + .filter(w -> w.getId().equals(workoutId)) + .findFirst() + .orElseThrow( + () -> new WorkoutNotFoundException("Workout not found with id: " + workoutId)); + + workouts.remove(workoutToRemove); + + TrainingSchedule updatedSchedule = trainingScheduleRepository.save(schedule); + return TrainingScheduleMapper.toDto(updatedSchedule); + } + + @Transactional + public TrainingScheduleDto addWorkoutToSchedule(WorkoutRequestDto workoutDto, UUID trainingScheduleId){ + Workout workout = WorkoutMapper.toModel(workoutDto); + + TrainingSchedule trainingSchedule = trainingScheduleRepository.findById(trainingScheduleId).orElseThrow( + () -> new TrainingScheduleNotFoundException("Training schedule not found with id: " + trainingScheduleId)); + + workout.setTrainingSchedule(trainingSchedule); + trainingSchedule.getWorkouts().add(workout); + + TrainingSchedule updatedSchedule = trainingScheduleRepository.save(trainingSchedule); + return TrainingScheduleMapper.toDto(updatedSchedule); + } +} + diff --git a/client-service/src/main/java/com/cm/clientservice/service/UserService.java b/client-service/src/main/java/com/cm/clientservice/service/UserService.java index d9ee028..233c3f3 100644 --- a/client-service/src/main/java/com/cm/clientservice/service/UserService.java +++ b/client-service/src/main/java/com/cm/clientservice/service/UserService.java @@ -3,10 +3,22 @@ import com.cm.clientservice.dto.OutgoingEmailUpdateDTO; import com.cm.clientservice.dto.UserRequestDTO; import com.cm.clientservice.dto.UserResponseDTO; -import com.cm.clientservice.exception.EmailAlreadyExistsException; -import com.cm.clientservice.exception.UserNotFoundException; +import com.cm.clientservice.dto.contract.*; +import com.cm.clientservice.dto.contract.template.AgreementTemplateRequestDto; +import com.cm.clientservice.dto.contract.template.AgreementTemplateResponseDto; +import com.cm.clientservice.dto.scheduling.TrainingScheduleDto; +import com.cm.clientservice.dto.scheduling.workout.WorkoutRequestDto; +import com.cm.clientservice.exception.contract.CoachClientAgreementNotFoundException; +import com.cm.clientservice.exception.users.EmailAlreadyExistsException; +import com.cm.clientservice.exception.schedule.UnauthorizedScheduleAccessException; +import com.cm.clientservice.exception.users.UserNotFoundException; +import com.cm.clientservice.mapper.contract.CoachClientAgreementMapper; +import com.cm.clientservice.mapper.schedule.TrainingScheduleMapper; import com.cm.clientservice.mapper.UserMapper; +import com.cm.clientservice.model.contract.CoachClientAgreement; +import com.cm.clientservice.model.schedule.TrainingSchedule; import com.cm.clientservice.repository.UserRepository; +import jakarta.transaction.Transactional; import org.springframework.beans.factory.annotation.Value; import org.springframework.http.HttpHeaders; import org.springframework.stereotype.Service; @@ -21,17 +33,43 @@ public class UserService { private final UserRepository userRepository; + private final CoachClientAgreementService coachClientAgreementService; + private final TrainingScheduleService trainingScheduleService; + private final AgreementTemplateService agreementTemplateService; private final WebClient webClient; public UserService(UserRepository userRepository, WebClient.Builder webClientBuilder, - @Value("${auth.service.url}") String authServiceUrl){ + @Value("${auth.service.url}") String authServiceUrl, + CoachClientAgreementService coachClientAgreementService, + TrainingScheduleService trainingScheduleService, + AgreementTemplateService agreementTemplateService){ this.userRepository = userRepository; + this.agreementTemplateService = agreementTemplateService; this.webClient = webClientBuilder.baseUrl(authServiceUrl).build(); + this.coachClientAgreementService = coachClientAgreementService; + this.trainingScheduleService = trainingScheduleService; } + private User findCoachByAuthIdOrElseThrow(UUID coachAuthId){ + return userRepository.findByAuthId(coachAuthId).orElseThrow( + () -> new UserNotFoundException("Coach user not found with authId:" + coachAuthId)); + } + + private User findUserByAuthIdOrElseThrow(UUID userAuthId){ + return userRepository.findByAuthId(userAuthId).orElseThrow( + () -> new UserNotFoundException("User not found with authId:" + userAuthId)); + } + + private User findUserByIdOrElseThrow(UUID userId){ + return userRepository.findById(userId).orElseThrow( + () -> new UserNotFoundException("User not found with authId:" + userId)); + } + + + public UserResponseDTO createUser(UserRequestDTO userRequestDTO, String auth_id){ // Make a call to the repository to create a user. if(userRepository.existsByEmail(userRequestDTO.getEmail())){ @@ -41,6 +79,10 @@ public UserResponseDTO createUser(UserRequestDTO userRequestDTO, String auth_id) User newUser = UserMapper.toModel(userRequestDTO); newUser.setAuthId(UUID.fromString(auth_id)); + + TrainingSchedule schedule = new TrainingSchedule(); + newUser.setTrainingSchedule(schedule); + newUser = userRepository.save(newUser); return UserMapper.toDTO(newUser); @@ -58,18 +100,9 @@ public List getAllUsers(){ .toList(); } - public List getAllCoachUsers(){ - List users = userRepository.findByCoachProfileIsNotNull(); - - return users.stream() - .map(UserMapper::toDTO) - .toList(); - } - public UserResponseDTO updateUser(UUID authId, UserRequestDTO userRequestDTO, String token){ // Find the user that has the authId - User user = userRepository.findByAuthId(authId).orElseThrow( - () -> new UserNotFoundException("User not found with authId: " + authId)); + User user = findUserByAuthIdOrElseThrow(authId); boolean updatingEmail = !user.getEmail().equalsIgnoreCase(userRequestDTO.getEmail()); @@ -88,8 +121,7 @@ public UserResponseDTO updateUser(UUID authId, UserRequestDTO userRequestDTO, St outgoingEmailUpdateDTO.setOldEmail(user.getEmail()); outgoingEmailUpdateDTO.setNewEmail(userRequestDTO.getEmail()); - // Make a request to the auth service to update the email of the user with this email. - // Now check if the token they supplied was valid using our endpoint of auth service. + // Let auth service update their email for signing in. webClient.put() .uri("/auth/update-email") .header(HttpHeaders.AUTHORIZATION, token) @@ -109,5 +141,156 @@ public UserResponseDTO updateUser(UUID authId, UserRequestDTO userRequestDTO, St return UserMapper.toDTO(updatedUser); } + public List getClientsOfCoach(UUID coachAuthId){ + UUID coachId = findCoachByAuthIdOrElseThrow(coachAuthId).getId(); + + return coachClientAgreementService + .getCoachClientAgreements(coachId) + .stream() + .filter(coachClientAgreementService::isAgreementActive) + .map(a -> UserMapper.toDTO(a.getClient())) + .distinct() + .toList(); + } + + public List getCoachesOfClient(UUID clientAuthId) { + UUID clientId = findUserByAuthIdOrElseThrow(clientAuthId).getId(); + + + return coachClientAgreementService + .getCoachClientAgreements(clientId) + .stream() + .map(a -> UserMapper.toDTO(a.getCoach())) + .distinct() + .toList(); + } + + public TrainingScheduleDto getClientTrainingSchedule(UUID clientId, UUID coachAuthId){ + UUID coachId = findCoachByAuthIdOrElseThrow(coachAuthId).getId(); + + if (!coachClientAgreementService.isUserAClientOfCoach(clientId, coachId)) { + throw new UnauthorizedScheduleAccessException("The user with id: " + coachId + " is not a coach of user with id: " + clientId); + } + + TrainingSchedule clientSchedule = + findUserByIdOrElseThrow(clientId).getTrainingSchedule(); + + return TrainingScheduleMapper.toDto(clientSchedule); + } + + public TrainingScheduleDto getTrainingSchedule(UUID authId){ + TrainingSchedule schedule = findUserByAuthIdOrElseThrow(authId).getTrainingSchedule(); + return TrainingScheduleMapper.toDto(schedule); + } + + + public TrainingScheduleDto addWorkoutToSchedule(UUID clientAuthId, WorkoutRequestDto workoutDto) { + UUID trainingScheduleId = + findUserByAuthIdOrElseThrow(clientAuthId).getTrainingSchedule().getId(); + + return trainingScheduleService.addWorkoutToSchedule(workoutDto, trainingScheduleId); + } + + + public TrainingScheduleDto addWorkoutToClientsSchedule(UUID coachAuthId, UUID clientId, WorkoutRequestDto workoutDto) { + UUID coachId = findCoachByAuthIdOrElseThrow(coachAuthId).getId(); + if(!coachClientAgreementService.isUserAClientOfCoach(clientId, coachId)){ + throw new UnauthorizedScheduleAccessException("The user with id: " + coachId + " is not a coach of user with id: " + clientId); + } + + UUID trainingScheduleId = + findUserByIdOrElseThrow(clientId).getTrainingSchedule().getId(); + + return trainingScheduleService.addWorkoutToSchedule(workoutDto, trainingScheduleId); + } + + + public TrainingScheduleDto removeWorkoutFromClientSchedule(UUID coachAuthId, UUID clientId, UUID workoutId) { + UUID coachId = findCoachByAuthIdOrElseThrow(coachAuthId).getId(); + + if(!coachClientAgreementService.isUserAClientOfCoach(clientId, coachId)){ + throw new UnauthorizedScheduleAccessException("The user with id: " + coachId + " is not a coach of user with id: " + clientId); + } + + TrainingSchedule trainingSchedule = + findUserByIdOrElseThrow(clientId).getTrainingSchedule(); + + return trainingScheduleService.removeWorkoutFromSchedule(trainingSchedule, workoutId); + } + + public TrainingScheduleDto removeWorkoutFromSchedule(UUID clientAuthId, UUID workoutId) { + + TrainingSchedule trainingSchedule = + findUserByAuthIdOrElseThrow(clientAuthId).getTrainingSchedule(); + + return trainingScheduleService.removeWorkoutFromSchedule(trainingSchedule, workoutId); + } + + + public CoachClientAgreementResponseDto proposeCoachClientAgreement(UUID coachAuthId, + UUID clientId, + CoachClientAgreementRequestDto agreementDto) { + + User coach = findCoachByAuthIdOrElseThrow(coachAuthId); + User client = findUserByIdOrElseThrow(clientId); + + return coachClientAgreementService + .proposeCoachClientAgreement(coach, client, agreementDto); + } + + public CoachClientAgreementResponseDto withdrawCoachesAgreementAcceptance(UUID coachAuthId, UUID agreementId) { + User coach = findCoachByAuthIdOrElseThrow(coachAuthId); + + CoachClientAgreement agreementToChange = + coach.getAgreementsAsCoach() + .stream() + .filter(a -> a.getId().equals(agreementId)) + .findFirst() + .orElseThrow(() -> + new CoachClientAgreementNotFoundException( + "Coach client agreement owned by coach id: " + + coach.getId() + " with agreement id: " + agreementId + " not found.")); + + return coachClientAgreementService.withdrawCoachesAgreementAcceptance(agreementToChange); + } + + public List getCoachAgreementTemplates(UUID coachAuthId) { + User coach = findCoachByAuthIdOrElseThrow(coachAuthId); + return agreementTemplateService.findAgreementsAuthoredByCoach(coach); + + } + + public AgreementTemplateResponseDto createAgreementTemplate(UUID coachAuthId, AgreementTemplateRequestDto templateRequestDto) { + User coach = findCoachByAuthIdOrElseThrow(coachAuthId); + return agreementTemplateService.createAgreementTemplate(coach, templateRequestDto); + } + + public List getPendingAgreements(UUID authId) { + UUID userId = findUserByAuthIdOrElseThrow(authId).getId(); + + return coachClientAgreementService + .getCoachClientAgreements(userId) + .stream() + .filter(coachClientAgreementService::isAgreementPending) + .map(CoachClientAgreementMapper::toDto) + .toList(); + } + + @Transactional + public CoachClientAgreementResponseDto setAcceptanceStatusOfPendingAgreement(UUID clientAuthId, UUID agreementId, ContractAgreementStatusRequestDto agreementRequest) { + User client = findUserByAuthIdOrElseThrow(clientAuthId); + + CoachClientAgreement agreementToChange = + client.getAgreementsAsClient() + .stream() + .filter(a -> a.getId().equals(agreementId)) + .findFirst() + .orElseThrow(() -> + new CoachClientAgreementNotFoundException( + "Coach client agreement owned by user id: " + + client.getId() + " with agreement id: " + agreementId + " not found.")); + + return coachClientAgreementService.setClientAgreementStatus(agreementToChange, agreementRequest); + } } diff --git a/client-service/src/main/resources/data.sql b/client-service/src/main/resources/data.sql deleted file mode 100644 index fa4f92d..0000000 --- a/client-service/src/main/resources/data.sql +++ /dev/null @@ -1,24 +0,0 @@ --- Ensure the 'users' table exists -CREATE TABLE IF NOT EXISTS users -( - id UUID PRIMARY KEY, - first_name VARCHAR(255) NOT NULL, - last_name VARCHAR(255) NOT NULL, - email VARCHAR(255) UNIQUE NOT NULL, - address VARCHAR(255) NOT NULL, - date_of_birth DATE NOT NULL, - registration_date DATE NOT NULL - ); - --- Insert well-known UUIDs for specific users -INSERT INTO users (id, first_name, last_name, email, address, date_of_birth, registration_date) -SELECT '123e4567-e89b-12d3-a456-426614174000', - 'Larry', - 'David', - 'larry.david@example.com', - '123 Main St, Springfield', - '1985-06-15', - '2024-01-10' - WHERE NOT EXISTS (SELECT 1 - FROM users - WHERE id = '123e4567-e89b-12d3-a456-426614174000'); diff --git a/client-service/src/test/java/com/cm/clientservice/CoachingControllerTests.java b/client-service/src/test/java/com/cm/clientservice/CoachingControllerTests.java new file mode 100644 index 0000000..7ee06fa --- /dev/null +++ b/client-service/src/test/java/com/cm/clientservice/CoachingControllerTests.java @@ -0,0 +1,288 @@ +package com.cm.clientservice; +import com.cm.clientservice.dto.UserRequestDTO; +import com.cm.clientservice.dto.contract.CoachClientAgreementRequestDto; +import com.cm.clientservice.dto.contract.ContractAgreementStatusRequestDto; +import com.cm.clientservice.dto.contract.template.AgreementTemplateRequestDto; +import com.cm.clientservice.dto.contract.template.AgreementTemplateReuseRequestDto; +import com.cm.clientservice.dto.scheduling.exercise.ExerciseRequestDto; +import com.cm.clientservice.dto.scheduling.movement.MovementCreateRequestDto; +import com.cm.clientservice.dto.scheduling.movement.MovementResponseDto; +import com.cm.clientservice.dto.scheduling.movement.MovementReuseRequestDto; +import com.cm.clientservice.dto.scheduling.workout.WorkoutRequestDto; +import com.cm.clientservice.model.User; +import com.cm.clientservice.repository.UserRepository; +import com.cm.clientservice.service.AgreementTemplateService; +import com.cm.clientservice.service.CoachClientAgreementService; +import com.cm.clientservice.service.MovementService; +import io.restassured.RestAssured; +import io.restassured.http.ContentType; + +import java.util.List; +import java.util.UUID; + +import io.restassured.response.Response; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.test.web.server.LocalServerPort; +import org.springframework.test.context.DynamicPropertyRegistry; +import org.springframework.test.context.DynamicPropertySource; +import org.testcontainers.containers.PostgreSQLContainer; +import com.cm.clientservice.service.UserService; +import org.testcontainers.junit.jupiter.Container; +import org.testcontainers.junit.jupiter.Testcontainers; +import org.testcontainers.shaded.org.checkerframework.checker.units.qual.A; + +@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) +@Testcontainers +public class CoachingControllerTests { + + @LocalServerPort + private Integer port; + + @Autowired + private UserService userService; + + @Autowired + private UserRepository userRepository; + + @Autowired + private CoachClientAgreementService coachClientAgreementService; + + @Autowired + private AgreementTemplateService agreementTemplateService; + + @Autowired + MovementService movementService; + + @Container + static PostgreSQLContainer postgres = + new PostgreSQLContainer<>("postgres:latest") + .withDatabaseName("testdb") + .withUsername("user") + .withPassword("pass"); + + @DynamicPropertySource + static void configureProperties(DynamicPropertyRegistry registry) { + registry.add("spring.datasource.url", postgres::getJdbcUrl); + registry.add("spring.datasource.username", postgres::getUsername); + registry.add("spring.datasource.password", postgres::getPassword); + registry.add("spring.jpa.hibernate.ddl-auto", () -> "update"); + registry.add("spring.jpa.hibernate.ddl-auto", () -> "create-drop"); + registry.add("spring.datasource.driver-class-name", () -> "org.postgresql.Driver"); + } + + @BeforeEach + void setUp(){ + RestAssured.baseURI = "http://localhost:" + port + "/clients/coaching"; + userRepository.deleteAll(); + } + + UUID createCoachUserInDbReturnAuthId(){ + UserRequestDTO userRequest = new UserRequestDTO(); + userRequest.setAddress("225 TestCoach Lane, Testville, 45135, Testax, United States of Testing."); + userRequest.setEmail("test_coach@test.com"); + userRequest.setDateOfBirth("1970-01-01"); + userRequest.setFirstName("Test"); + userRequest.setLastName("Coach"); + userRequest.setRegisteredDate("2025-10-10"); + + UUID mockAuthId = UUID.randomUUID(); + + userService.createUser(userRequest, String.valueOf(mockAuthId)); + return mockAuthId; + } + + UUID createClientUserInDBReturnAuthId(){ + UserRequestDTO userRequest = new UserRequestDTO(); + userRequest.setAddress("45 TestClient Lane, Testville, 45135, Testax, United States of Testing."); + userRequest.setEmail("test_client@test.com"); + userRequest.setDateOfBirth("1970-01-01"); + userRequest.setFirstName("Test"); + userRequest.setLastName("Client"); + userRequest.setRegisteredDate("2025-09-09"); + + UUID mockAuthId = UUID.randomUUID(); + + userService.createUser(userRequest, String.valueOf(mockAuthId)); + return mockAuthId; + } + + UUID createMockMovementReturnId(){ + // make movement + MovementCreateRequestDto dto = new MovementCreateRequestDto(); + dto.setDescription("This is a fake movement."); + dto.setName("Test Movement"); + + // return id of created. + MovementResponseDto responseDto = movementService.createMovement(dto); + return UUID.fromString(responseDto.getId()); + } + + ExerciseRequestDto createExerciseRequestDto(UUID movementId){ + // movement + MovementReuseRequestDto movementReuse = new MovementReuseRequestDto(); + movementReuse.setId(String.valueOf(movementId)); + + // exercise + ExerciseRequestDto dto = new ExerciseRequestDto(); + dto.setMovement(movementReuse); + dto.setCoachNotes("Mock Coach Notes"); + dto.setNumReps("1"); + dto.setNumSets("2"); + return dto; + } + + UUID createWorkoutInUserScheduleAndReturnScheduleId(User client, List exerciseRequestDtos){ + WorkoutRequestDto dto = new WorkoutRequestDto(); + dto.setDate("2025-05-05"); + dto.setWorkoutNotes("Test workout notes"); + dto.setIsCompleted("TRUE"); + dto.setExercises(exerciseRequestDtos); + dto.setTrainingScheduleId(String.valueOf(client.getTrainingSchedule().getId())); + UUID scheduleId = UUID.fromString(userService.addWorkoutToSchedule(client.getAuthId(), dto).getId()); + return scheduleId; + } + + UUID createCoachClientAgreementBetweenCoachAndClientReturnId(UUID coachAuth, UUID clientAuth) { + User coach = userRepository.findByAuthId(coachAuth).get(); + User client = userRepository.findByAuthId(clientAuth).get(); + + // template + AgreementTemplateReuseRequestDto template = new AgreementTemplateReuseRequestDto(); + UUID templateId = createMockAgreementTemplateReturnId(coach); + template.setId(templateId.toString()); + + // request + CoachClientAgreementRequestDto requestDto = new CoachClientAgreementRequestDto(); + requestDto.setStartDate("2025-05-05"); + requestDto.setTemplate(template); + + // agreement + UUID agreementId = UUID.fromString(coachClientAgreementService.proposeCoachClientAgreement(coach, client, requestDto).getId()); + + // client accept + ContractAgreementStatusRequestDto status = new ContractAgreementStatusRequestDto(); + status.setUserIsInAgreement(Boolean.TRUE); + userService.setAcceptanceStatusOfPendingAgreement(clientAuth, agreementId, status); + + return agreementId; + } + + UUID createMockAgreementTemplateReturnId(User coach){ + AgreementTemplateRequestDto dto = new AgreementTemplateRequestDto(); + dto.setTemplateName("Mock Template"); + dto.setPaymentAmount("100.00"); + dto.setDaysBetweenPayments("10"); + dto.setAllowPublicTemplateReuse("TRUE"); + dto.setTermsAndConditions("Mock terms"); + + return UUID.fromString(agreementTemplateService.createAgreementTemplate(coach, dto).getId()); + } + + + + // getClients + @Test + public void getClientsWithNoClientsShouldReturnEmptyList() { + UUID mockAuthId = createCoachUserInDbReturnAuthId(); + + // Call controller endpoint + Response response = + RestAssured.given() + .header("X-AUTH-ID", mockAuthId.toString()) + .accept(ContentType.JSON) + .when() + .get("/clients") + .then() + .statusCode(200) + .extract() + .response(); + + // Verify returned JSON is an empty list. + List list = response.jsonPath().getList("$"); + assert list.isEmpty(); + } + + + + // getClientSchedule + @Test + public void getClientScheduleWithValidClientAndOneWorkoutShouldReturnOneWorkout(){ + + // Create client and coach + UUID coachAuthId = createCoachUserInDbReturnAuthId(); + UUID clientAuthId = createClientUserInDBReturnAuthId(); + + User client = userRepository.findByAuthId(clientAuthId).get(); + + // Create a workout in the clients schedule + UUID movementId = createMockMovementReturnId(); + ExerciseRequestDto exerciseRequestDto = createExerciseRequestDto(movementId); + createWorkoutInUserScheduleAndReturnScheduleId(client, List.of(exerciseRequestDto)); + + // Create an agreement between client and coach. + UUID agreementId = createCoachClientAgreementBetweenCoachAndClientReturnId(coachAuthId, clientAuthId); + + Response response = + RestAssured.given() + .header("X-AUTH-ID", coachAuthId.toString()) + .accept(ContentType.JSON) + .when() + .get("/clients/" + client.getId() + "/schedule") + .then() + .statusCode(200) + .extract() + .response(); + + // Verify returned JSON is an empty list. + List workouts = response.jsonPath().getList("workouts"); + assert workouts.size() == 1; + } + + + // deleteWorkoutFromClientSchedule + @Test + public void deleteWorkoutFromClientScheduleWithValidClientAndOneWorkout(){ + // Needs: + // Coach user + // Client user + // training schedule, with 1 workout + + + // Then -> Delete the workout by clientId and workoutId + + // Assert: getClientSchedule(clientId) has 0 workouts in it. + } + + + // addWorkoutToClientSchedule + @Test + public void addWorkoutToClientScheduleValidClientShouldAddOneWorkout(){ + // Needs: + + // User coach + // User client + // training schedule with 0 workouts. + + // CoachClientAgreement between coach and client. + // a valid AgreementTemplate + + // A workout + // A movement for the workout to use. + + + // Then -> add the workout to client schedule + + + // Assert that the workout was added correctly. 1 workout and fields were right. + } + +// proposeClientAgreement + + +// withdrawCoachesAgreementAcceptance +// createAgreementTemplate +// getCoachesAgreementTemplates +} diff --git a/integration-testing/src/test/java/CoachingIntegrationTests.java b/integration-testing/src/test/java/CoachingIntegrationTests.java new file mode 100644 index 0000000..bf22bd6 --- /dev/null +++ b/integration-testing/src/test/java/CoachingIntegrationTests.java @@ -0,0 +1,2 @@ +package PACKAGE_NAME;public class CoachingIntegrationTests { +} diff --git a/integration-testing/src/test/java/ClientIntegrationTest.java b/integration-testing/src/test/java/UserIntegrationTests.java similarity index 100% rename from integration-testing/src/test/java/ClientIntegrationTest.java rename to integration-testing/src/test/java/UserIntegrationTests.java