-
Notifications
You must be signed in to change notification settings - Fork 3
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #468 from puzzle/feature/331-objective-draftstate
Feature/331: Objective Draft State and Release
- Loading branch information
Showing
37 changed files
with
1,078 additions
and
54 deletions.
There are no files selected for viewing
42 changes: 42 additions & 0 deletions
42
backend/src/main/java/ch/puzzle/okr/controller/CompletedController.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,42 @@ | ||
package ch.puzzle.okr.controller; | ||
|
||
import ch.puzzle.okr.models.Completed; | ||
import ch.puzzle.okr.service.business.CompletedBusinessService; | ||
import io.swagger.v3.oas.annotations.Operation; | ||
import io.swagger.v3.oas.annotations.media.Content; | ||
import io.swagger.v3.oas.annotations.media.Schema; | ||
import io.swagger.v3.oas.annotations.responses.ApiResponse; | ||
import io.swagger.v3.oas.annotations.responses.ApiResponses; | ||
import org.springframework.http.HttpStatus; | ||
import org.springframework.http.ResponseEntity; | ||
import org.springframework.web.bind.annotation.*; | ||
|
||
@RestController | ||
@RequestMapping("api/v2/completed") | ||
public class CompletedController { | ||
|
||
private final CompletedBusinessService completedBusinessService; | ||
|
||
public CompletedController(CompletedBusinessService completedBusinessService) { | ||
this.completedBusinessService = completedBusinessService; | ||
} | ||
|
||
@Operation(summary = "Create Completed", description = "Create a new Completed Reference.") | ||
@ApiResponses(value = { | ||
@ApiResponse(responseCode = "201", description = "Created new Completed.", content = { | ||
@Content(mediaType = "application/json", schema = @Schema(implementation = Completed.class)) }), | ||
@ApiResponse(responseCode = "404", description = "Could not create Completed Reference", content = @Content) }) | ||
@PostMapping | ||
public ResponseEntity<Completed> createCompleted(@RequestBody Completed completed) { | ||
Completed createdCompleted = completedBusinessService.createCompleted(completed); | ||
return ResponseEntity.status(HttpStatus.CREATED).body(createdCompleted); | ||
} | ||
|
||
@Operation(summary = "Delete Completed by Objective Id", description = "Delete Completed Reference by Objective Id") | ||
@ApiResponses(value = { @ApiResponse(responseCode = "200", description = "Deleted Completed by Objective Id"), | ||
@ApiResponse(responseCode = "404", description = "Did not find the Completed with requested Objective id") }) | ||
@DeleteMapping("/{objectiveId}") | ||
public void deleteCompletedByObjectiveId(@PathVariable long objectiveId) { | ||
completedBusinessService.deleteCompletedByObjectiveId(objectiveId); | ||
} | ||
} |
102 changes: 102 additions & 0 deletions
102
backend/src/main/java/ch/puzzle/okr/models/Completed.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,102 @@ | ||
package ch.puzzle.okr.models; | ||
|
||
import javax.persistence.*; | ||
import javax.validation.constraints.NotNull; | ||
import javax.validation.constraints.Size; | ||
import java.util.Objects; | ||
|
||
@Entity | ||
public class Completed { | ||
@Id | ||
@GeneratedValue(strategy = GenerationType.AUTO, generator = "sequence_objective") | ||
private Long id; | ||
|
||
@NotNull(message = "Objective must not be null") | ||
@OneToOne | ||
private Objective objective; | ||
|
||
@Size(max = 4096, message = "Attribute comment has a max length of 4096 characters when completing an objective") | ||
private String comment; | ||
|
||
public Completed() { | ||
} | ||
|
||
private Completed(Builder builder) { | ||
id = builder.id; | ||
setObjective(builder.objective); | ||
setComment(builder.comment); | ||
} | ||
|
||
public Long getId() { | ||
return id; | ||
} | ||
|
||
public Objective getObjective() { | ||
return objective; | ||
} | ||
|
||
public void setObjective(Objective objective) { | ||
this.objective = objective; | ||
} | ||
|
||
public String getComment() { | ||
return comment; | ||
} | ||
|
||
public void setComment(String comment) { | ||
this.comment = comment; | ||
} | ||
|
||
@Override | ||
public String toString() { | ||
return "Completed{" + "id=" + id + ", objective=" + objective + ", comment='" + comment + '\'' + '}'; | ||
} | ||
|
||
@Override | ||
public boolean equals(Object o) { | ||
if (this == o) | ||
return true; | ||
if (o == null || getClass() != o.getClass()) | ||
return false; | ||
Completed completed = (Completed) o; | ||
return Objects.equals(id, completed.id) && Objects.equals(objective, completed.objective) | ||
&& Objects.equals(comment, completed.comment); | ||
} | ||
|
||
@Override | ||
public int hashCode() { | ||
return Objects.hash(id, objective, comment); | ||
} | ||
|
||
public static final class Builder { | ||
private Long id; | ||
private Objective objective; | ||
private String comment; | ||
|
||
private Builder() { | ||
} | ||
|
||
public static Builder builder() { | ||
return new Builder(); | ||
} | ||
|
||
public Builder withId(Long id) { | ||
this.id = id; | ||
return this; | ||
} | ||
|
||
public Builder withObjective(Objective objective) { | ||
this.objective = objective; | ||
return this; | ||
} | ||
|
||
public Builder withComment(String comment) { | ||
this.comment = comment; | ||
return this; | ||
} | ||
|
||
public Completed build() { | ||
return new Completed(this); | ||
} | ||
} | ||
} |
8 changes: 8 additions & 0 deletions
8
backend/src/main/java/ch/puzzle/okr/repository/CompletedRepository.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
package ch.puzzle.okr.repository; | ||
|
||
import ch.puzzle.okr.models.Completed; | ||
import org.springframework.data.repository.CrudRepository; | ||
|
||
public interface CompletedRepository extends CrudRepository<Completed, Long> { | ||
Completed findByObjectiveId(Long objectiveId); | ||
} |
33 changes: 33 additions & 0 deletions
33
backend/src/main/java/ch/puzzle/okr/service/business/CompletedBusinessService.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
package ch.puzzle.okr.service.business; | ||
|
||
import ch.puzzle.okr.models.Completed; | ||
import ch.puzzle.okr.service.persistence.CompletedPersistenceService; | ||
import ch.puzzle.okr.service.validation.CompletedValidationService; | ||
import org.springframework.stereotype.Service; | ||
|
||
import javax.transaction.Transactional; | ||
|
||
@Service | ||
public class CompletedBusinessService { | ||
private final CompletedPersistenceService completedPersistenceService; | ||
private final CompletedValidationService validator; | ||
|
||
public CompletedBusinessService(CompletedPersistenceService completedPersistenceService, | ||
CompletedValidationService validator) { | ||
this.completedPersistenceService = completedPersistenceService; | ||
this.validator = validator; | ||
} | ||
|
||
@Transactional | ||
public Completed createCompleted(Completed completed) { | ||
validator.validateOnCreate(completed); | ||
return completedPersistenceService.save(completed); | ||
} | ||
|
||
@Transactional | ||
public void deleteCompletedByObjectiveId(Long objectiveId) { | ||
Completed completed = completedPersistenceService.getCompletedByObjectiveId(objectiveId); | ||
validator.validateOnDelete(completed.getId()); | ||
completedPersistenceService.deleteById(completed.getId()); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
22 changes: 22 additions & 0 deletions
22
backend/src/main/java/ch/puzzle/okr/service/persistence/CompletedPersistenceService.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
package ch.puzzle.okr.service.persistence; | ||
|
||
import ch.puzzle.okr.models.Completed; | ||
import ch.puzzle.okr.repository.CompletedRepository; | ||
import org.springframework.stereotype.Service; | ||
|
||
@Service | ||
public class CompletedPersistenceService extends PersistenceBase<Completed, Long> { | ||
|
||
protected CompletedPersistenceService(CompletedRepository repository) { | ||
super(repository); | ||
} | ||
|
||
@Override | ||
public String getModelName() { | ||
return "Completed"; | ||
} | ||
|
||
public Completed getCompletedByObjectiveId(Long objectiveId) { | ||
return ((CompletedRepository) this.repository).findByObjectiveId(objectiveId); | ||
} | ||
} |
25 changes: 25 additions & 0 deletions
25
backend/src/main/java/ch/puzzle/okr/service/validation/CompletedValidationService.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
package ch.puzzle.okr.service.validation; | ||
|
||
import ch.puzzle.okr.models.Completed; | ||
import ch.puzzle.okr.service.persistence.CompletedPersistenceService; | ||
import org.springframework.stereotype.Service; | ||
|
||
@Service | ||
public class CompletedValidationService extends ValidationBase<Completed, Long> { | ||
|
||
public CompletedValidationService(CompletedPersistenceService completedPersistenceService) { | ||
super(completedPersistenceService); | ||
} | ||
|
||
@Override | ||
public void validateOnCreate(Completed model) { | ||
throwExceptionIfModelIsNull(model); | ||
throwExceptionWhenIdIsNotNull(model.getId()); | ||
validate(model); | ||
} | ||
|
||
@Override | ||
public void validateOnUpdate(Long id, Completed model) { | ||
throw new IllegalCallerException("This method must not be called because there is no update of completed"); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -7,6 +7,7 @@ truncate table person; | |
truncate table quarter; | ||
truncate table team; | ||
truncate table alignment; | ||
truncate table completed; | ||
|
||
SET REFERENTIAL_INTEGRITY TRUE; | ||
|
||
|
@@ -17,6 +18,7 @@ ALTER SEQUENCE sequence_objective RESTART WITH 200; | |
ALTER SEQUENCE sequence_key_result RESTART WITH 200; | ||
ALTER SEQUENCE sequence_check_in RESTART WITH 200; | ||
ALTER SEQUENCE sequence_alignment RESTART WITH 200; | ||
ALTER SEQUENCE sequence_completed RESTART WITH 200; | ||
|
||
insert into person (id, email, firstname, lastname, username) | ||
values (1, '[email protected]', 'Paco', 'Eggimann', 'peggimann'), | ||
|
@@ -109,4 +111,9 @@ insert into alignment (id, aligned_objective_id, alignment_type, target_key_resu | |
(1, 4, 'objective', null, 3), | ||
(2, 4, 'keyResult', 8, null); | ||
|
||
); | ||
); | ||
|
||
insert into completed (id, objective_id, comment) values | ||
(1, 4, 'Das hat geklappt'), | ||
(2, 6, 'War leider nicht moeglich'), | ||
(3, 10, 'Schade'); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
10 changes: 10 additions & 0 deletions
10
backend/src/main/resources/db/migration/V2_0_5__createCompletedTable.sql
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
create sequence if not exists sequence_completed; | ||
|
||
create table if not exists completed | ||
( | ||
id bigint not null primary key, | ||
objective_id bigint not null | ||
constraint fk_completed_objective | ||
references objective, | ||
comment varchar(4096) | ||
); |
80 changes: 80 additions & 0 deletions
80
backend/src/test/java/ch/puzzle/okr/controller/CompleteControllerIT.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,80 @@ | ||
package ch.puzzle.okr.controller; | ||
|
||
import ch.puzzle.okr.models.Completed; | ||
import ch.puzzle.okr.models.Objective; | ||
import ch.puzzle.okr.service.business.CompletedBusinessService; | ||
import org.hamcrest.core.Is; | ||
|
||
import org.junit.jupiter.api.Test; | ||
import org.junit.jupiter.api.extension.ExtendWith; | ||
import org.mockito.BDDMockito; | ||
import org.mockito.junit.jupiter.MockitoExtension; | ||
import org.springframework.beans.factory.annotation.Autowired; | ||
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest; | ||
import org.springframework.boot.test.mock.mockito.MockBean; | ||
import org.springframework.http.HttpStatus; | ||
import org.springframework.http.MediaType; | ||
import org.springframework.security.test.context.support.WithMockUser; | ||
import org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors; | ||
import org.springframework.test.web.servlet.MockMvc; | ||
import org.springframework.test.web.servlet.result.MockMvcResultMatchers; | ||
import org.springframework.web.server.ResponseStatusException; | ||
|
||
import static ch.puzzle.okr.KeyResultTestHelpers.*; | ||
import static org.mockito.ArgumentMatchers.any; | ||
import static org.mockito.ArgumentMatchers.anyLong; | ||
import static org.mockito.Mockito.doThrow; | ||
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*; | ||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath; | ||
|
||
@WithMockUser(value = "spring") | ||
@ExtendWith(MockitoExtension.class) | ||
@WebMvcTest(CompletedController.class) | ||
class CompleteControllerIT { | ||
|
||
@MockBean | ||
CompletedBusinessService completedBusinessService; | ||
|
||
@Autowired | ||
private MockMvc mvc; | ||
|
||
Completed successfulCompleted = Completed.Builder.builder().withId(1L) | ||
.withObjective(Objective.Builder.builder().withId(3L).withTitle("Gute Lernende").build()) | ||
.withComment("Wir haben es gut geschafft").build(); | ||
|
||
public static final String createBodySuccessful = """ | ||
{ | ||
"id":null, | ||
"objectiveId":3, | ||
"comment":"Wir haben es gut geschafft" | ||
} | ||
"""; | ||
|
||
String baseUrl = "/api/v2/completed"; | ||
|
||
@Test | ||
void createSuccessfulCompleted() throws Exception { | ||
BDDMockito.given(this.completedBusinessService.createCompleted((any()))).willReturn(successfulCompleted); | ||
|
||
mvc.perform(post(baseUrl).content(createBodySuccessful).contentType(MediaType.APPLICATION_JSON) | ||
.with(SecurityMockMvcRequestPostProcessors.csrf())) | ||
.andExpect(MockMvcResultMatchers.status().is2xxSuccessful()).andExpect(jsonPath(JSON_PATH_ID, Is.is(1))) | ||
.andExpect(jsonPath("$.objective.id", Is.is(3))) | ||
.andExpect(jsonPath("$.comment", Is.is("Wir haben es gut geschafft"))); | ||
} | ||
|
||
@Test | ||
void shouldDeleteCompleted() throws Exception { | ||
mvc.perform(delete("/api/v2/completed/1").with(SecurityMockMvcRequestPostProcessors.csrf())) | ||
.andExpect(MockMvcResultMatchers.status().isOk()); | ||
} | ||
|
||
@Test | ||
void throwExceptionWhenCompletedWithIdCantBeFoundWhileDeleting() throws Exception { | ||
doThrow(new ResponseStatusException(HttpStatus.NOT_FOUND, "Completed not found")).when(completedBusinessService) | ||
.deleteCompletedByObjectiveId(anyLong()); | ||
|
||
mvc.perform(delete("/api/v2/completed/1000").with(SecurityMockMvcRequestPostProcessors.csrf())) | ||
.andExpect(MockMvcResultMatchers.status().isNotFound()); | ||
} | ||
} |
Oops, something went wrong.