Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
16 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
package school.faang.user_service.controller.goal;

import jakarta.validation.Valid;
import lombok.RequiredArgsConstructor;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PatchMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import school.faang.user_service.entity.filter.GoalFilterDto;
import school.faang.user_service.entity.goal.Goal;
import school.faang.user_service.entity.goal.dto.request.CreateGoalDto;
import school.faang.user_service.entity.goal.dto.request.UpdateGoalDto;
import school.faang.user_service.entity.goal.dto.response.GoalDto;
import school.faang.user_service.entity.goal.mapper.GoalMapper;
import school.faang.user_service.service.goal.GoalService;

import java.util.List;

@RestController
@RequestMapping("/api/v1/goals")
@RequiredArgsConstructor
public class GoalController {

private final GoalService goalService;
private final GoalMapper goalMapper;

@GetMapping("/{goalId}")
public ResponseEntity<GoalDto> getGoal(@PathVariable long goalId) {
Goal goal = goalService.getGoalById(goalId);
return ResponseEntity.ok(goalMapper.toGoalDto(goal));
}

@PostMapping("/filter")
public ResponseEntity<List<GoalDto>> getGoals(@RequestBody GoalFilterDto goalFilterDto) {
List<Goal> filteredGoals = goalService.getGoalsByFilter(goalFilterDto);
return ResponseEntity.ok(goalMapper.toGoalDtoList(filteredGoals));
}

@PostMapping("/{parentId}/subGoals/filter")
public ResponseEntity<List<GoalDto>> getSubGoals(@PathVariable long parentId, @RequestBody GoalFilterDto goalFilterDto) {
List<Goal> filteredSubGoals = goalService.getSubGoalsByFilter(parentId, goalFilterDto);
return ResponseEntity.ok(goalMapper.toGoalDtoList(filteredSubGoals));
}

@PostMapping
public ResponseEntity<GoalDto> createGoal(@RequestBody @Valid CreateGoalDto goalDto) {
Goal createdGoal = goalService.createGoal(
goalMapper.toGoal(goalDto),
goalDto.skillsId(),
goalDto.parentId()
);
return ResponseEntity
.status(HttpStatus.CREATED)
.body(goalMapper.toGoalDto(createdGoal));
}

@PatchMapping("/{goalId}")
public ResponseEntity<GoalDto> updateGoal(@PathVariable long goalId, @RequestBody @Valid UpdateGoalDto goalDto) {
Goal createdGoal = goalService.update(
goalId,
goalMapper.toGoal(goalDto),
goalDto.skillsId()
);
return ResponseEntity.ok(goalMapper.toGoalDto(createdGoal));
}

@DeleteMapping("/{goalId}")
public ResponseEntity<Void> deleteGoal(@PathVariable long goalId) {
goalService.delete(goalId);
return ResponseEntity.noContent().build();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package school.faang.user_service.entity.filter;

import com.fasterxml.jackson.annotation.JsonInclude;
import school.faang.user_service.entity.goal.GoalStatus;

import java.time.LocalDateTime;
import java.util.List;

@JsonInclude(JsonInclude.Include.NON_EMPTY)
public record GoalFilterDto(
List<Long> usersId,
String title,
GoalStatus status,
List<Long> skillsId,
LocalDateTime deadline
) {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package school.faang.user_service.entity.goal.dto.request;

import com.fasterxml.jackson.annotation.JsonInclude;
import jakarta.validation.constraints.Future;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;

import java.time.LocalDateTime;
import java.util.List;

@JsonInclude(JsonInclude.Include.NON_NULL)
public record CreateGoalDto(
@NotBlank(message = "Empty goal title not allowed!") String title,
String description,
Long parentId,
@NotNull List<Long> skillsId,
@Future(message = "Deadline must be in future!") LocalDateTime deadline
) {}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package school.faang.user_service.entity.goal.dto.request;

import com.fasterxml.jackson.annotation.JsonInclude;
import jakarta.validation.constraints.Future;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import school.faang.user_service.entity.goal.GoalStatus;

import java.time.LocalDateTime;
import java.util.List;

@JsonInclude(JsonInclude.Include.NON_NULL)
public record UpdateGoalDto(
@NotBlank(message = "Empty goal title not allowed!") String title,

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

можно не писать подробные сообщения

String description,
GoalStatus status,
@NotNull List<Long> skillsId,
@Future(message = "Dead line must be in future!") LocalDateTime deadline
) {}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package school.faang.user_service.entity.goal.dto.response;

import com.fasterxml.jackson.annotation.JsonInclude;
import school.faang.user_service.entity.goal.GoalStatus;

import java.time.LocalDateTime;
import java.util.List;

@JsonInclude(JsonInclude.Include.NON_NULL)
public record GoalDto(
Long id,
String title,
String description,
Long parentId,
GoalStatus status,
List<Long> skillsId,
LocalDateTime deadline
) {}
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
package school.faang.user_service.entity.goal.mapper;

import org.mapstruct.Mapper;
import org.mapstruct.Mapping;
import org.mapstruct.MappingConstants;
import school.faang.user_service.entity.Skill;
import school.faang.user_service.entity.goal.Goal;
import school.faang.user_service.entity.goal.dto.request.CreateGoalDto;
import school.faang.user_service.entity.goal.dto.request.UpdateGoalDto;
import school.faang.user_service.entity.goal.dto.response.GoalDto;

import java.util.List;

@Mapper(componentModel = MappingConstants.ComponentModel.SPRING)
public interface GoalMapper {

@Mapping(source = "parent.id", target = "parentId")
@Mapping(target = "skillsId", expression = "java(mapSkillsToIds(goal.getSkillsToAchieve()))")
GoalDto toGoalDto(Goal goal);

@Mapping(target = "id", ignore = true)
@Mapping(target = "status", ignore = true)
@Mapping(target = "parent", ignore = true)
@Mapping(target = "skillsToAchieve", ignore = true)
@Mapping(target = "createdAt", ignore = true)
@Mapping(target = "updatedAt", ignore = true)
@Mapping(target = "mentor", ignore = true)
@Mapping(target = "invitations", ignore = true)
@Mapping(target = "users", ignore = true)
Goal toGoal(CreateGoalDto goalDto);

@Mapping(target = "id", ignore = true)
@Mapping(target = "parent", ignore = true)
@Mapping(target = "skillsToAchieve", ignore = true)
@Mapping(target = "createdAt", ignore = true)
@Mapping(target = "updatedAt", ignore = true)
@Mapping(target = "mentor", ignore = true)
@Mapping(target = "invitations", ignore = true)
@Mapping(target = "users", ignore = true)
Goal toGoal(UpdateGoalDto goalDto);

default List<Long> mapSkillsToIds(List<Skill> skills) {
return skills.stream()
.map(Skill::getId)
.toList();
}

List<GoalDto> toGoalDtoList(List<Goal> goals);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package school.faang.user_service.exception.goal;

public class GoalNotExistException extends RuntimeException {

public GoalNotExistException(long goalId) {
super("Goal with id %d not exist".formatted(goalId));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package school.faang.user_service.exception.goal;

public class MaxActiveGoalPerUserException extends RuntimeException {

public MaxActiveGoalPerUserException(Long userId, int goalLimit) {
super("User with id %s reach max goals active limit of %d".formatted(userId, goalLimit));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package school.faang.user_service.exception.goal;

public class UpdateComleteGoalException extends RuntimeException {

public UpdateComleteGoalException(long goalId) {
super("Goal with id %d is complete. Update not allowed.".formatted(goalId));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package school.faang.user_service.exception.goal;

public class UpdateGoalWithActiveSubGoalsException extends RuntimeException {

public UpdateGoalWithActiveSubGoalsException(long goalId, String activeSubGoalsId) {
super("Goal with id %d have active sub goals [%s]. Update not allowed.".formatted(goalId, activeSubGoalsId));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package school.faang.user_service.exception.goal;

public class UserNotGoalOwnerException extends RuntimeException {

public UserNotGoalOwnerException(long userId, long goalId) {
super("User with id %d not the owner of goal with id %d".formatted(userId, goalId));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package school.faang.user_service.exception.skill;

public class SkillNotExistException extends RuntimeException {

public SkillNotExistException(String skillsId) {
super("Contains skills [%s] that not exist!!".formatted(skillsId));
}

public SkillNotExistException(Long skillId) {
super("Skill with id - '%d' not exist!!".formatted(skillId));
}
}
8 changes: 8 additions & 0 deletions src/main/java/school/faang/user_service/filter/Filter.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package school.faang.user_service.filter;

public interface Filter<T, P> {

boolean isApplicable(T filterDto);

P apply(P filteredData, T filterDto);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package school.faang.user_service.filter.goal;

import school.faang.user_service.entity.filter.GoalFilterDto;
import school.faang.user_service.entity.goal.Goal;
import school.faang.user_service.filter.Filter;

import java.util.stream.Stream;

public interface GoalFilter extends Filter<GoalFilterDto, Stream<Goal>> {}
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package school.faang.user_service.filter.goal;

import org.springframework.stereotype.Component;
import school.faang.user_service.entity.filter.GoalFilterDto;
import school.faang.user_service.entity.goal.Goal;

import java.util.Objects;
import java.util.stream.Stream;

@Component
public class GoalFilterBySkills implements GoalFilter {

@Override
public boolean isApplicable(GoalFilterDto filterDto) {
return Objects.nonNull(filterDto.skillsId()) && !filterDto.skillsId().isEmpty();
}

@Override
public Stream<Goal> apply(Stream<Goal> filteredData, GoalFilterDto filterDto) {
return filteredData.filter(goal -> Objects.nonNull(goal.getSkillsToAchieve())
&& goal.getSkillsToAchieve()
.stream()
.anyMatch(skill -> filterDto.skillsId().contains(skill.getId()))
);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package school.faang.user_service.filter.goal;

import org.springframework.stereotype.Component;
import school.faang.user_service.entity.filter.GoalFilterDto;
import school.faang.user_service.entity.goal.Goal;

import java.util.Objects;
import java.util.stream.Stream;

@Component
public class GoalFilterByStatus implements GoalFilter {

@Override
public boolean isApplicable(GoalFilterDto filterDto) {
return Objects.nonNull(filterDto.status());
}

@Override
public Stream<Goal> apply(Stream<Goal> filteredData, GoalFilterDto filterDto) {
return filteredData.filter(goal -> Objects.nonNull(goal.getStatus())
&& goal.getStatus() == filterDto.status());
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package school.faang.user_service.filter.goal;

import org.springframework.stereotype.Component;
import school.faang.user_service.entity.filter.GoalFilterDto;
import school.faang.user_service.entity.goal.Goal;

import java.util.Objects;
import java.util.stream.Stream;

@Component
public class GoalFilterByTitle implements GoalFilter {

@Override
public boolean isApplicable(GoalFilterDto filterDto) {
return Objects.nonNull(filterDto.title());
}

@Override
public Stream<Goal> apply(Stream<Goal> filteredData, GoalFilterDto filterDto) {
return filteredData.filter(goal -> Objects.nonNull(goal.getTitle())
&& goal.getTitle()
.toLowerCase()
.contains(filterDto.title().toLowerCase()));
}
}
Loading