Skip to content

Commit

Permalink
Feature/614 duplicate selected keyresults (#1216)
Browse files Browse the repository at this point in the history
* create new endpoint in Objectivecontroller

* crate endpoint for getAllKeyResultsById

* modify duplicate e2e to accept new duplicateObjectDto now only selected keyresults get added

* create new DuplicateObjectiveDto

* implement new duplication to frontend

* remove files added accidentally

* change duplicateObjective type to record instead of class

* fix bug with id of objective already being defined when trying to duplicate

* fix bug that you couldnt edit objectives anymore

* fix bug that you can't add objectives anymore

* Fix small typo

* Reset run config

* Fix behaviour of duplicating objectives and small typo

* Fix logic with creating and duplicating objective

* Implement functionality to only show text at desired time and try to fix tests

* Fix failing method calls for tests in backend

* Cleanup ngOnInit in the objective-form component

* Fix backend tests

* Fix frontend tests

* Reset ngOnInit since it caused a few problems and fix broken TestData

* Add method for e2e testing and first two e2e tests. Second one is not finished yet. Plus correct styling for selecting the keyresults

* [FM] Automated formating frontend

* make all objectiveTests work again except duplication

* Finish e2e tests for duplicating

* Add e2e test for duplicating a objective with no keyresults

* Fix integration test

* Add missing service tests for newly created methods

* Cleanup ngOnInit a bit

* Add comments and two small tests for ts

* Add more descriptive naming in describe

Co-authored-by: MasterEvarior <puzzle@giannin.ch>

* Fix grammar in controller endpoint

* Clenup cypress helper and use variable in styles

* Remove not needed method

* Fix alignment, truncating of text and add tooltip

* Set padding to 0 to align correctly

* Fix color from failed rebase

---------

Co-authored-by: Manuel <moeri@puzzle.ch>
Co-authored-by: GitHub Actions <actions@github.com>
Co-authored-by: Giannin <puzzle@giannin.ch>
Co-authored-by: Manuel <109959820+ManuelMoeri@users.noreply.github.com>
  • Loading branch information
5 people authored Dec 13, 2024
1 parent 500a2e3 commit 4f0882a
Show file tree
Hide file tree
Showing 18 changed files with 503 additions and 135 deletions.
Original file line number Diff line number Diff line change
@@ -1,8 +1,15 @@
package ch.puzzle.okr.controller;

import ch.puzzle.okr.dto.checkin.CheckInDto;
import ch.puzzle.okr.dto.keyresult.KeyResultDto;
import ch.puzzle.okr.dto.DuplicateObjectiveDto;
import ch.puzzle.okr.dto.ObjectiveDto;
import ch.puzzle.okr.mapper.ObjectiveMapper;
import ch.puzzle.okr.mapper.keyresult.KeyResultMapper;
import ch.puzzle.okr.models.Action;
import ch.puzzle.okr.models.Objective;
import ch.puzzle.okr.models.keyresult.KeyResult;
import ch.puzzle.okr.service.authorization.ActionAuthorizationService;
import ch.puzzle.okr.service.authorization.ObjectiveAuthorizationService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
Expand All @@ -14,6 +21,8 @@
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;

import java.util.List;

import static org.springframework.http.HttpStatus.IM_USED;
import static org.springframework.http.HttpStatus.OK;

Expand All @@ -22,11 +31,16 @@
public class ObjectiveController {
private final ObjectiveAuthorizationService objectiveAuthorizationService;
private final ObjectiveMapper objectiveMapper;
private final KeyResultMapper keyResultMapper;
private final ActionAuthorizationService actionAuthorizationService;

public ObjectiveController(ObjectiveAuthorizationService objectiveAuthorizationService,
ObjectiveMapper objectiveMapper) {
ObjectiveMapper objectiveMapper, KeyResultMapper keyResultMapper,
ActionAuthorizationService actionAuthorizationService) {
this.objectiveAuthorizationService = objectiveAuthorizationService;
this.objectiveMapper = objectiveMapper;
this.keyResultMapper = keyResultMapper;
this.actionAuthorizationService = actionAuthorizationService;
}

@Operation(summary = "Get Objective", description = "Get an Objective by ID")
Expand All @@ -42,6 +56,22 @@ public ResponseEntity<ObjectiveDto> getObjective(
.body(objectiveMapper.toDto(objectiveAuthorizationService.getEntityById(id)));
}

@Operation(summary = "Get KeyResults from Objective", description = "Get all KeyResults from one Objective by ObjectiveId.")
@ApiResponses(value = {
@ApiResponse(responseCode = "200", description = "Returned all KeyResults from Objective.", content = {
@Content(mediaType = "application/json", schema = @Schema(implementation = CheckInDto.class)) }),
@ApiResponse(responseCode = "401", description = "Not authorized to read KeyResults from an Objective", content = @Content),
@ApiResponse(responseCode = "404", description = "Did not find an Objective with the specified ID to get KeyResults from.", content = @Content) })
@GetMapping("/{id}/keyResults")
public ResponseEntity<List<KeyResultDto>> getKeyResultsFromObjective(
@Parameter(description = "The ID for getting all KeyResults of an Objective", required = true) @PathVariable long id) {
return ResponseEntity.status(OK)
.body(objectiveAuthorizationService.getAllKeyResultsByObjective(id).stream().map(keyResult -> {
List<Action> actionList = actionAuthorizationService.getActionsByKeyResult(keyResult);
return keyResultMapper.toDto(keyResult, actionList);
}).toList());
}

@Operation(summary = "Delete Objective by ID", description = "Delete Objective by ID")
@ApiResponses(value = { @ApiResponse(responseCode = "200", description = "Deleted Objective by ID"),
@ApiResponse(responseCode = "401", description = "Not authorized to delete an Objective", content = @Content),
Expand Down Expand Up @@ -72,10 +102,12 @@ public ResponseEntity<ObjectiveDto> createObjective(
@PostMapping("/{id}")
public ResponseEntity<ObjectiveDto> duplicateObjective(
@Parameter(description = "The ID for duplicating an Objective.", required = true) @PathVariable Long id,
@io.swagger.v3.oas.annotations.parameters.RequestBody(description = "The Objective which should be duplicated as json", required = true) @RequestBody ObjectiveDto objectiveDTO) {
Objective objective = objectiveMapper.toObjective(objectiveDTO);
@io.swagger.v3.oas.annotations.parameters.RequestBody(description = "The Objective which should be duplicated as JSON", required = true) @RequestBody DuplicateObjectiveDto duplicateObjectiveDto) {
Objective objective = objectiveMapper.toObjective(duplicateObjectiveDto.objective());
List<KeyResult> keyResults = duplicateObjectiveDto.keyResults().stream().map(keyResultMapper::toKeyResult)
.toList();
ObjectiveDto duplicatedObjectiveDto = objectiveMapper
.toDto(objectiveAuthorizationService.duplicateEntity(id, objective));
.toDto(objectiveAuthorizationService.duplicateEntity(id, objective, keyResults));
return ResponseEntity.status(HttpStatus.CREATED).body(duplicatedObjectiveDto);
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package ch.puzzle.okr.dto;

import ch.puzzle.okr.dto.keyresult.KeyResultDto;

import java.util.List;

public record DuplicateObjectiveDto(ObjectiveDto objective, List<KeyResultDto> keyResults) {
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,12 @@

import ch.puzzle.okr.models.Objective;
import ch.puzzle.okr.models.authorization.AuthorizationUser;
import ch.puzzle.okr.models.keyresult.KeyResult;
import ch.puzzle.okr.service.business.ObjectiveBusinessService;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import java.util.List;

@Service
public class ObjectiveAuthorizationService extends AuthorizationServiceBase<Long, Objective, ObjectiveBusinessService> {
Expand All @@ -13,10 +17,16 @@ public ObjectiveAuthorizationService(ObjectiveBusinessService objectiveBusinessS
super(objectiveBusinessService, authorizationService);
}

public Objective duplicateEntity(Long id, Objective objective) {
public Objective duplicateEntity(Long id, Objective objective, List<KeyResult> keyResults) {
AuthorizationUser authorizationUser = getAuthorizationService().updateOrAddAuthorizationUser();
hasRoleCreateOrUpdate(objective, authorizationUser);
return getBusinessService().duplicateObjective(id, objective, authorizationUser);
return getBusinessService().duplicateObjective(id, objective, authorizationUser, keyResults);
}

public List<KeyResult> getAllKeyResultsByObjective(Long objectiveId) {
AuthorizationUser authorizationUser = getAuthorizationService().updateOrAddAuthorizationUser();
getAuthorizationService().hasRoleReadByObjectiveId(objectiveId, authorizationUser);
return getBusinessService().getAllKeyResultsByObjective(objectiveId);
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,13 +22,12 @@

@Service
public class ObjectiveBusinessService implements BusinessServiceInterface<Long, Objective> {
private static final Logger logger = LoggerFactory.getLogger(ObjectiveBusinessService.class);
private final ObjectivePersistenceService objectivePersistenceService;
private final ObjectiveValidationService validator;
private final KeyResultBusinessService keyResultBusinessService;
private final CompletedBusinessService completedBusinessService;

private static final Logger logger = LoggerFactory.getLogger(ObjectiveBusinessService.class);

public ObjectiveBusinessService(@Lazy KeyResultBusinessService keyResultBusinessService,
ObjectiveValidationService validator, ObjectivePersistenceService objectivePersistenceService,
CompletedBusinessService completedBusinessService) {
Expand All @@ -38,6 +37,10 @@ public ObjectiveBusinessService(@Lazy KeyResultBusinessService keyResultBusiness
this.completedBusinessService = completedBusinessService;
}

private static boolean hasQuarterChanged(Objective objective, Objective savedObjective) {
return !Objects.equals(objective.getQuarter(), savedObjective.getQuarter());
}

public Objective getEntityById(Long id) {
validator.validateOnGet(id);
return objectivePersistenceService.findById(id);
Expand All @@ -48,6 +51,11 @@ public List<Objective> getEntitiesByTeamId(Long id) {
return objectivePersistenceService.findObjectiveByTeamId(id);
}

public List<KeyResult> getAllKeyResultsByObjective(Long objectiveId) {
Objective objective = objectivePersistenceService.findById(objectiveId);
return keyResultBusinessService.getAllKeyResultsByObjective(objective.getId());
}

@Transactional
public Objective updateEntity(Long id, Objective objective, AuthorizationUser authorizationUser) {
Objective savedObjective = objectivePersistenceService.findById(id);
Expand Down Expand Up @@ -88,10 +96,6 @@ private boolean hasAlreadyCheckIns(Objective savedObjective) {
.anyMatch(kr -> keyResultBusinessService.hasKeyResultAnyCheckIns(kr.getId()));
}

private static boolean hasQuarterChanged(Objective objective, Objective savedObjective) {
return !Objects.equals(objective.getQuarter(), savedObjective.getQuarter());
}

@Transactional
public Objective createEntity(Objective objective, AuthorizationUser authorizationUser) {
objective.setCreatedBy(authorizationUser.user());
Expand All @@ -110,13 +114,16 @@ public Objective createEntity(Objective objective, AuthorizationUser authorizati
* New Objective with no KeyResults
* @param authorizationUser
* AuthorizationUser
* @param keyResults
* KeyResults to copy
*
* @return New Objective with copied KeyResults form the source Objective
*/
@Transactional
public Objective duplicateObjective(Long id, Objective objective, AuthorizationUser authorizationUser) {
public Objective duplicateObjective(Long id, Objective objective, AuthorizationUser authorizationUser,
List<KeyResult> keyResults) {
Objective duplicatedObjective = createEntity(objective, authorizationUser);
for (KeyResult keyResult : keyResultBusinessService.getAllKeyResultsByObjective(id)) {
for (KeyResult keyResult : keyResults) {
duplicateKeyResult(authorizationUser, keyResult, duplicatedObjective);
}
return duplicatedObjective;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,7 @@ insert into key_result (id, version, baseline, description, modified_on, stretch
values (10,1, 465, '', '2023-07-25 08:23:02.273028', 60, 'Im Durchschnitt soll die Lautstärke 60dB nicht überschreiten', 1, 5, 1, 'metric', '2023-07-25 08:23:02.273028', 'PERCENT', null, null, null),
(8,1, 213425, '', '2023-07-25 08:19:44.351252', 80, 'High employee satisfaction scores (80%+) throughout the year.', 1, 4, 1, 'metric', '2023-07-25 08:19:44.351252', 'PERCENT', null, null, null),
(7,1, 84, '', '2023-07-25 08:19:13.569300', 4, 'Monthly town halls between our people and leadership teams over the next four months.', 1, 4, 1, 'metric', '2023-07-25 08:19:13.569300', 'PERCENT', null, null, null),
(6,1, 5, '', '2023-07-25 08:18:44.087674', 1, 'New structure that rewards funny guys and innovation before the end of Q1. ', 1, 4, 1, 'metric', '2023-07-25 08:18:44.087674', 'PERCENT', null, null, null),
(6,1, 5, '', '2023-07-25 08:18:44.087674', 1, 'New structure that rewards funny guys and innovation before the end of Q1.', 1, 4, 1, 'metric', '2023-07-25 08:18:44.087674', 'PERCENT', null, null, null),
(15,1, 0, 'asf', '2023-07-25 08:40:49.412684', 1, ' Lorem ipsum dolor sit amet', 1, 9, 1, 'metric', '2023-07-25 08:40:49.412684', 'PERCENT', null, null, null),
(5,1, null, '', '2023-07-25 08:16:24.466383', null, 'Kundenzufriedenheitsumfrage soll mindestens einmal pro 2 Wochen durchgeführt werden. ', 1, 3, 1, 'ordinal', '2023-07-25 08:16:24.466383', 'PERCENT', 'Fisch', 'Hai', 'MEG'),
(12,1, 0, '', '2023-07-25 08:28:45.110759', 80, 'Wir wollen bereits nach Q1 rund 80% des Redesigns vom OKR-Tool abgeschlossen haben. ', 1, 6, 1, 'metric', '2023-07-25 08:28:45.110759', 'PERCENT', null, null, null),
Expand All @@ -123,9 +123,9 @@ values (1,1, 'Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam
(3,1, 'Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores ', '2023-07-25 08:44:54.832400', '', '2023-07-24 22:00:00.000000', 1, 1, 7, 5, 'metric', null),
(4,1, 'Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores ', '2023-07-25 08:45:07.911215', 'Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores ', '2023-07-25 22:00:00.000000', 7, 1, 7, 5, 'metric', null),
(5,1, 'Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores ', '2023-07-25 08:45:25.583267', 'Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores ', '2023-07-24 22:00:00.000000', 3, 1, 6, 5, 'metric', null),
(6,1, 'Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores ', '2023-07-25 08:45:42.707340', 'Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores ', '2023-07-24 22:00:00.000000', 3, 1, 5, 5, 'metric', null),
(7,1, 'Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores ', '2023-07-25 08:45:57.304875', '', '2023-07-25 22:00:00.000000', 2, 1, 5, 5, 'metric', null),
(8,1, 'Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores ', '2023-07-25 08:46:21.358930', 'Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores ', '2023-07-24 22:00:00.000000', 70, 1, 4, 5, 'metric', null),
(6,1, 'Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores ', '2023-07-25 08:45:42.707340', 'Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores ', '2023-07-24 22:00:00.000000', null, 1, 5, 5, 'ordinal', 'COMMIT'),
(7,1, 'Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores ', '2023-07-25 08:45:57.304875', '', '2023-07-25 22:00:00.000000', null, 1, 5, 5, 'ordinal', 'COMMIT'),
(8,1, 'Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores ', '2023-07-25 08:46:21.358930', 'Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores ', '2023-07-24 22:00:00.000000', null, 1, 4, 5, 'ordinal', 'COMMIT'),
(9,1, 'Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores ', '2023-07-25 08:46:39.204525', 'Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores ', '2023-07-24 22:00:00.000000', 16, 1, 3, 5, 'metric', null),
(10,1, 'Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores ', '2023-07-25 08:47:01.649202', 'Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores ', '2023-07-24 22:00:00.000000', 10, 1, 14, 5, 'metric', null),
(11,1, 'Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores ', '2023-07-25 08:47:22.341767', 'Mehr Kuchen', '2023-07-24 22:00:00.000000', 1, 1, 13, 5, 'metric', null),
Expand Down
Loading

0 comments on commit 4f0882a

Please sign in to comment.