Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
Expand Up @@ -5,6 +5,7 @@
import jakarta.validation.constraints.NotEmpty;
import jakarta.validation.constraints.NotNull;
import pl.edu.agh.project_manager.controller.dto.milestone.MilestoneRequest;
import pl.edu.agh.project_manager.controller.dto.project.projectrisk.ProjectRiskRequest;
import pl.edu.agh.project_manager.service.command.project.ProjectCreationCommand;

import java.time.LocalDate;
Expand Down Expand Up @@ -33,7 +34,7 @@ public record ProjectCreationRequest(
List<UUID> committee,

@Valid
List<RiskRequest> risks,
List<ProjectRiskRequest> risks,

@Valid
List<MilestoneRequest> milestones
Expand All @@ -50,7 +51,7 @@ public ProjectCreationCommand toCommand(UUID creatorId) {
this.startDate,
this.endDate,
this.projectGroupId,
this.risks.stream().map(RiskRequest::toCommand).toList(),
this.risks.stream().map(ProjectRiskRequest::toCommand).toList(),
this.milestones.stream().map(MilestoneRequest::toCommand).toList(),
this.sponsors,
this.committee
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,26 +1,31 @@
package pl.edu.agh.project_manager.controller.dto.project;
package pl.edu.agh.project_manager.controller.dto.project.projectrisk;

import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import org.hibernate.validator.constraints.Range;
import pl.edu.agh.project_manager.service.command.project.RiskCommand;

public record RiskRequest(
public record ProjectRiskRequest(
@NotBlank(message = "Nazwa ryzyka nie może być pusta!")
String name,

@NotBlank(message = "Opis ryzyka nie może być pusty!")
String description,

@NotNull(message = "Poziom prawdopodobieństwa jest wymagany!")
@Range(min = 0, max = 100, message = "Prawdopodobieństwo musi być w skali 0-100%!")
Integer probability
@Range(min = 1, max = 5, message = "Prawdopodobieństwo musi być w skali 1-5!")
Integer probability,

@NotNull(message = "Poziom wpływu jest wymagany!")
@Range(min = 1, max = 5, message = "Wpływ musi być w skali 1-5!")
Integer impact
) {
public RiskCommand toCommand() {
return new RiskCommand(
this.name,
this.description,
this.probability
this.probability,
this.impact
);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package pl.edu.agh.project_manager.controller.dto.project.projectrisk;

import pl.edu.agh.project_manager.domain.entity.project.ProjectRisk;

import java.util.UUID;

public record ProjectRiskResponse(
UUID id,
String name,
String description,
Integer probability,
Integer impact,
Integer value
) {
public static ProjectRiskResponse from(ProjectRisk risk) {
return new ProjectRiskResponse(
risk.getId(),
risk.getName(),
risk.getDescription(),
risk.getProbability(),
risk.getImpact(),
risk.getProbability() * risk.getImpact()
);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@
import org.springframework.http.ResponseEntity;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.*;
import pl.edu.agh.project_manager.controller.dto.project.RiskRequest;
import pl.edu.agh.project_manager.controller.dto.project.RiskResponse;
import pl.edu.agh.project_manager.controller.dto.project.projectrisk.ProjectRiskRequest;
import pl.edu.agh.project_manager.controller.dto.project.projectrisk.ProjectRiskResponse;
import pl.edu.agh.project_manager.service.project.ProjectRiskService;

import java.util.List;
Expand All @@ -21,11 +21,11 @@ public class ProjectRiskController {

@PostMapping
@PreAuthorize("@projectAccess.isProjectManagerForProject(#projectId, authentication.principal)")
public ResponseEntity<RiskResponse> createProjectRisk(
public ResponseEntity<ProjectRiskResponse> createProjectRisk(
@PathVariable UUID projectId,
@Valid @RequestBody RiskRequest riskRequest
@Valid @RequestBody ProjectRiskRequest riskRequest
) {
RiskResponse createdRisk = riskService.createProjectRisk(
ProjectRiskResponse createdRisk = riskService.createProjectRisk(
riskRequest.toCommand(), projectId
);

Expand All @@ -34,21 +34,21 @@ public ResponseEntity<RiskResponse> createProjectRisk(

@GetMapping
@PreAuthorize("hasAnyRole('ADMINISTRATOR', 'AUTHORITY') or @projectAccess.canAccessProject(#projectId, authentication.principal)")
public ResponseEntity<List<RiskResponse>> getRisks(
public ResponseEntity<List<ProjectRiskResponse>> getRisks(
@PathVariable UUID projectId
) {
List<RiskResponse> risks = riskService.getProjectRisks(projectId);
List<ProjectRiskResponse> risks = riskService.getProjectRisks(projectId);
return ResponseEntity.ok(risks);
}

@PatchMapping("/{riskId}")
@PreAuthorize("@projectAccess.isProjectManagerForProject(#projectId, authentication.principal)")
public ResponseEntity<RiskResponse> updateProjectRisk(
public ResponseEntity<ProjectRiskResponse> updateProjectRisk(
@PathVariable UUID projectId,
@PathVariable UUID riskId,
@Valid @RequestBody RiskRequest riskRequest
@Valid @RequestBody ProjectRiskRequest riskRequest
) {
RiskResponse updatedRisk = riskService.updateProjectRisk(
ProjectRiskResponse updatedRisk = riskService.updateProjectRisk(
projectId,
riskId,
riskRequest.toCommand()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,13 +26,17 @@ public class ProjectRisk {
@Column(name = "description", nullable = false, length = 500)
private String description;

// TODO: Przejscie na model od 1 do 5 w skali prawdopodobienstwa wystapienia i skali ryzyka dla projektu
@Column(name = "probability", nullable = false, columnDefinition = "integer check (probability >= 0 and probability <= 100)")
@Min(value = 0, message = "Prawdopodobieństwo musi być większe bądź równe 0")
@Max(value = 100, message = "Prawdopodobieństwo musi być mniejsze bądź równe 100")
@Column(name = "probability", nullable = false, columnDefinition = "integer check (probability >= 1 and probability <= 5)")
@Min(value = 1, message = "Prawdopodobieństwo musi być w skali od 1 do 5")
@Max(value = 5, message = "Prawdopodobieństwo musi być w skali od 1 do 5")
private Integer probability;

@Column(name = "impact", nullable = false, columnDefinition = "integer check (impact >= 1 and impact <= 5)")

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

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

Czy jak się to wprowadzi to nie będzie problemu dla ryzyk już istniejących? Trzeba będzie wyczyścić wszystko?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Bedzie trzeba

@Min(value = 1, message = "Wpływ musi być w skali od 1 do 5")
@Max(value = 5, message = "Wpływ musi być w skali od 1 do 5")
private Integer impact;

@ManyToOne(fetch = FetchType.LAZY, optional = false)
@JoinColumn(name = "project_id", nullable = false)
private Project project;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
public record RiskCommand(
String name,
String description,
Integer probability
Integer probability,
Integer impact
) {
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,11 @@
import jakarta.transaction.Transactional;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import pl.edu.agh.project_manager.controller.dto.project.RiskResponse;
import pl.edu.agh.project_manager.controller.dto.project.projectrisk.ProjectRiskResponse;
import pl.edu.agh.project_manager.domain.entity.project.Project;
import pl.edu.agh.project_manager.domain.entity.project.ProjectRisk;
import pl.edu.agh.project_manager.domain.exception.ApiErrorCode;
import pl.edu.agh.project_manager.domain.exception.ApplicationException;
import pl.edu.agh.project_manager.repository.project.ProjectRepository;
import pl.edu.agh.project_manager.repository.project.RiskRepository;
import pl.edu.agh.project_manager.service.command.project.RiskCommand;

Expand All @@ -23,22 +22,22 @@ public class ProjectRiskService {
private final ProjectService projectService;

@Transactional
public RiskResponse createProjectRisk(RiskCommand command, UUID projectId) {
public ProjectRiskResponse createProjectRisk(RiskCommand command, UUID projectId) {
Project project = projectService.getProjectEntityOrThrow(projectId);

ProjectRisk risk = buildRisk(command);
risk.setProject(project);

ProjectRisk savedRisk = riskRepository.save(risk);
return RiskResponse.from(savedRisk);
return ProjectRiskResponse.from(savedRisk);
}

public List<RiskResponse> getProjectRisks(UUID projectId) {
public List<ProjectRiskResponse> getProjectRisks(UUID projectId) {
projectService.checkProjectExistsOrThrow(projectId);

return riskRepository.findAllByProjectId(projectId)
.stream()
.map(RiskResponse::from)
.map(ProjectRiskResponse::from)
.toList();
}

Expand All @@ -54,7 +53,7 @@ public void deleteProjectRisk(UUID projectId, UUID riskId) {
}

@Transactional
public RiskResponse updateProjectRisk(UUID projectId, UUID riskId, RiskCommand command) {
public ProjectRiskResponse updateProjectRisk(UUID projectId, UUID riskId, RiskCommand command) {
ProjectRisk risk = getRiskOrThrow(riskId);

if (!risk.getProject().getId().equals(projectId)) {
Expand All @@ -64,8 +63,9 @@ public RiskResponse updateProjectRisk(UUID projectId, UUID riskId, RiskCommand c
if (command.name() != null) risk.setName(command.name());
if (command.description() != null) risk.setDescription(command.description());
if (command.probability() != null) risk.setProbability(command.probability());
if (command.impact() != null) risk.setImpact(command.impact());

return RiskResponse.from(risk);
return ProjectRiskResponse.from(risk);
}

private ProjectRisk getRiskOrThrow(UUID riskId) {
Expand All @@ -78,6 +78,7 @@ private ProjectRisk buildRisk(RiskCommand command) {
.name(command.name())
.description(command.description())
.probability(command.probability())
.impact(command.impact())
.build();
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,7 @@ private void addRisksToProject(Project project, List<RiskCommand> risks) {
.name(riskRequest.name())
.description(riskRequest.description())
.probability(riskRequest.probability())
.impact(riskRequest.impact())
.build();

project.addRisk(risk);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import pl.edu.agh.project_manager.controller.dto.project.RiskResponse;
import pl.edu.agh.project_manager.controller.dto.project.projectrisk.ProjectRiskResponse;
import pl.edu.agh.project_manager.domain.entity.project.Project;
import pl.edu.agh.project_manager.domain.entity.project.ProjectRisk;
import pl.edu.agh.project_manager.repository.project.RiskRepository;
Expand Down Expand Up @@ -57,48 +57,56 @@ void updateProjectRisk_Success() {
// Given
UUID projectId = UUID.randomUUID();
UUID riskId = UUID.randomUUID();
RiskCommand command = new RiskCommand("New Name", "New Desc", 90);
RiskCommand command = new RiskCommand("New Name", "New Desc", 4, 5);

Project project = Project.builder().id(projectId).build();
ProjectRisk risk = ProjectRisk.builder()
.id(riskId)
.name("Old")
.project(project)
.probability(1)
.impact(1)
.build();

when(riskRepository.findById(riskId)).thenReturn(Optional.of(risk));

// When
RiskResponse response = riskService.updateProjectRisk(projectId, riskId, command);
ProjectRiskResponse response = riskService.updateProjectRisk(projectId, riskId, command);

// Then
assertThat(response.name()).isEqualTo("New Name");
assertThat(risk.getName()).isEqualTo("New Name");
assertThat(risk.getProbability()).isEqualTo(90);
assertThat(risk.getProbability()).isEqualTo(4);
assertThat(risk.getImpact()).isEqualTo(5);
assertThat(response.value()).isEqualTo(20);
}

@Test
@DisplayName("Should add new risk to existing project")
void createProjectRisk_Success() {
// Given
UUID projectId = UUID.randomUUID();
RiskCommand command = new RiskCommand("Title", "Desc", 50);
RiskCommand command = new RiskCommand("Title", "Desc", 3, 4);

Project project = Project.builder().id(projectId).build();
ProjectRisk savedRisk = ProjectRisk.builder()
.id(UUID.randomUUID())
.name("Title")
.description("Desc")
.probability(3)
.impact(4)
.project(project)
.build();

when(projectService.getProjectEntityOrThrow(projectId)).thenReturn(project);
when(riskRepository.save(any(ProjectRisk.class))).thenReturn(savedRisk);

// When
RiskResponse response = riskService.createProjectRisk(command, projectId);
ProjectRiskResponse response = riskService.createProjectRisk(command, projectId);

// Then
assertThat(response.id()).isEqualTo(savedRisk.getId());
assertThat(response.value()).isEqualTo(12);
verify(riskRepository).save(any(ProjectRisk.class));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ void createProject_Success() {
LocalDate.now(),
null,
null,
new ArrayList<>(List.of(new RiskCommand("Risk", "Desc", 50))),
new ArrayList<>(List.of(new RiskCommand("Risk", "Desc", 5, 3))),
new ArrayList<>(List.of(new MilestoneCommand("Start", "Start desc", LocalDate.now()))),
new ArrayList<>(), // Sponsors
new ArrayList<>() // Committee
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,8 @@ export const CreateProjectForm = () => {
},
});

const { data: groups = [] } = useProjectGroups();
const { data: groupsData = [] } = useProjectGroups();
const groups = groupsData.map(g => ({ id: g.id, name: g.name }));

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

potrzebne to?


const [sponsorsQuery, setSponsorsQuery] = useState("");
const [sponsorsQueryValue] = useDebounce(sponsorsQuery, 300);
Expand All @@ -44,11 +45,7 @@ export const CreateProjectForm = () => {
const navigate = useNavigate();

const onSubmit = methods.handleSubmit((data) => {
const payload = {
...data,
};

mutation.mutate(payload, {
mutation.mutate(data, {
onSuccess: (newProjectId) => {
methods.reset();
navigate(PATHS.PROJECT(newProjectId));
Expand Down
Loading
Loading