Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

DEV-189 - sprints crud #113

Merged
merged 11 commits into from
May 11, 2024
Original file line number Diff line number Diff line change
Expand Up @@ -98,8 +98,8 @@ public final SprintResponse deleteSprint(@RequestParam long sprintId, @JwtAuthed

@Override
@GetMapping(value = CURRENT_AND_FUTURE_SPRINTS)
public List<SprintResponse> getCurrentAndFutureSprints(@RequestParam long projectId,
@JwtAuthed User user) {
public final List<SprintResponse> getCurrentAndFutureSprints(@RequestParam long projectId,
@JwtAuthed User user) {
return sprintService.getCurrentAndFutureSprints(projectId, user);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import dev.corn.cornbackend.api.sprint.data.SprintRequest;
import dev.corn.cornbackend.api.sprint.data.SprintResponse;
import dev.corn.cornbackend.api.sprint.interfaces.SprintService;
import dev.corn.cornbackend.entities.backlog.item.interfaces.BacklogItemRepository;
import dev.corn.cornbackend.entities.project.Project;
import dev.corn.cornbackend.entities.project.interfaces.ProjectRepository;
import dev.corn.cornbackend.entities.sprint.Sprint;
Expand All @@ -13,6 +14,7 @@
import dev.corn.cornbackend.utils.exceptions.sprint.InvalidSprintDateException;
import dev.corn.cornbackend.utils.exceptions.sprint.SprintDoesNotExistException;
import dev.corn.cornbackend.utils.exceptions.sprint.SprintEndDateMustBeAfterStartDate;
import jakarta.transaction.Transactional;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.domain.Page;
Expand All @@ -38,10 +40,11 @@ public class SprintServiceImpl implements SprintService {
private static final String SPRINTS_ON_PAGE = "Sprints found on page : {}";
private final SprintRepository sprintRepository;
private final ProjectRepository projectRepository;
private final BacklogItemRepository backlogItemRepository;
private final SprintMapper sprintMapper;

@Override
public final SprintResponse addNewSprint(SprintRequest sprintRequest, User user) {
public SprintResponse addNewSprint(SprintRequest sprintRequest, User user) {

if (sprintRequest.endDate().isBefore(sprintRequest.startDate())) {
throw new SprintEndDateMustBeAfterStartDate(sprintRequest.startDate(), sprintRequest.endDate());
Expand Down Expand Up @@ -73,7 +76,7 @@ public final SprintResponse addNewSprint(SprintRequest sprintRequest, User user)
}

@Override
public final SprintResponse getSprintById(long sprintId, User user) {
public SprintResponse getSprintById(long sprintId, User user) {
log.info("Getting sprint with id: {} for user: {}", sprintId, user);

Sprint sprint = resolveSprintForProjectMember(sprintId, user);
Expand All @@ -84,7 +87,7 @@ public final SprintResponse getSprintById(long sprintId, User user) {
}

@Override
public final List<SprintResponse> getSprintsOnPage(int page, long projectId, User user) {
public List<SprintResponse> getSprintsOnPage(int page, long projectId, User user) {
log.info("Getting sprints in project {} on page: {} for user: {}", projectId, page, user);

Pageable pageable = PageRequest.of(page, SPRINTS_PER_PAGE);
Expand All @@ -97,7 +100,7 @@ public final List<SprintResponse> getSprintsOnPage(int page, long projectId, Use
}

@Override
public final SprintResponse updateSprintsName(String name, long sprintId, User user) {
public SprintResponse updateSprintsName(String name, long sprintId, User user) {
log.info("Updating sprint with id: {} name to: {}", sprintId, name);

Sprint sprintToUpdate = sprintRepository.findByIdWithProjectOwner(sprintId, user)
Expand All @@ -115,7 +118,7 @@ public final SprintResponse updateSprintsName(String name, long sprintId, User u
}

@Override
public final SprintResponse updateSprintsDescription(String description, long sprintId, User user) {
public SprintResponse updateSprintsDescription(String description, long sprintId, User user) {
log.info("Updating sprint with id: {} description to: {}", sprintId, description);

Sprint sprintToUpdate = sprintRepository.findByIdWithProjectOwner(sprintId, user)
Expand All @@ -133,10 +136,10 @@ public final SprintResponse updateSprintsDescription(String description, long sp
}

@Override
public final SprintResponse updateSprintsStartDate(LocalDate startDate, long sprintId, User user) {
public SprintResponse updateSprintsStartDate(LocalDate startDate, long sprintId, User user) {
log.info("Updating sprint with id: {} startDate to: {}", sprintId, startDate);

if (sprintRepository.existsEndDateBeforeDate(startDate)) {
if (sprintRepository.existsSprintPeriodWithGivenDate(startDate)) {
throw new InvalidSprintDateException("Start date cannot be after any existing sprint's end date");
}
Sprint sprintToUpdate = sprintRepository.findByIdWithProjectOwner(sprintId, user)
Expand All @@ -157,10 +160,10 @@ public final SprintResponse updateSprintsStartDate(LocalDate startDate, long spr
}

@Override
public final SprintResponse updateSprintsEndDate(LocalDate endDate, long sprintId, User user) {
public SprintResponse updateSprintsEndDate(LocalDate endDate, long sprintId, User user) {
log.info("Updating sprint with id: {} endDate to: {}", sprintId, endDate);

if (sprintRepository.existsEndDateBeforeDate(endDate)) {
if (sprintRepository.existsSprintPeriodWithGivenDate(endDate)) {
throw new InvalidSprintDateException("End date cannot be before any existing sprint's end date");
}
Sprint sprintToUpdate = sprintRepository.findByIdWithProjectOwner(sprintId, user)
Expand All @@ -181,14 +184,21 @@ public final SprintResponse updateSprintsEndDate(LocalDate endDate, long sprintI
}

@Override
public final SprintResponse deleteSprint(long sprintId, User user) {
@Transactional
public SprintResponse deleteSprint(long sprintId, User user) {
log.info("Deleting sprint with id: {}", sprintId);

Sprint sprintToDelete = sprintRepository.findByIdWithProjectOwner(sprintId, user)
.orElseThrow(() -> new SprintDoesNotExistException(sprintId));

log.info("Found sprint to delete: {}", sprintToDelete);

log.info("Moving all sprints backlog items to backlog...");

backlogItemRepository.updateSprintItemsToBacklog(sprintToDelete);

log.info("Deleting sprint...");

sprintRepository.deleteById(sprintId);

log.info("Deleted sprint with id: {}", sprintToDelete);
Expand Down Expand Up @@ -217,7 +227,7 @@ public List<SprintResponse> getCurrentAndFutureSprints(long projectId, User user
}

@Override
public final Page<SprintResponse> getSprintsAfterSprint(long sprintId, Pageable pageable, User user) {
public Page<SprintResponse> getSprintsAfterSprint(long sprintId, Pageable pageable, User user) {
log.info("Getting sprints after: {}", sprintId);

Sprint sprint = resolveSprintForProjectMember(sprintId, user);
Expand All @@ -233,7 +243,7 @@ public final Page<SprintResponse> getSprintsAfterSprint(long sprintId, Pageable
}

@Override
public final Page<SprintResponse> getSprintsBeforeSprint(long sprintId, Pageable pageable, User user) {
public Page<SprintResponse> getSprintsBeforeSprint(long sprintId, Pageable pageable, User user) {
log.info("Getting sprints before: {}", sprintId);

Sprint sprint = resolveSprintForProjectMember(sprintId, user);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,17 @@ public interface BacklogItemRepository extends JpaRepository<BacklogItem, Long>
*/
Page<BacklogItem> findByProjectAndSprintIsNull(Project project, Pageable pageable);

/**
* Moves all backlog items from sprint to backlog
*
* @param sprint Sprint to move BacklogItems from
*/
@Modifying
@Query("""
UPDATE BacklogItem b SET b.sprint = null WHERE b.sprint = :sprint
""")
void updateSprintItemsToBacklog(Sprint sprint);

/**
* Unassigns all BacklogItems associated with a ProjectMember
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -94,9 +94,9 @@ select count(s) > 0 from Sprint s
*/
@Query("""
select count(*) > 0 from Sprint s
where s.endDate < :date
where s.endDate <= :date and s.startDate >= :date
""")
boolean existsEndDateBeforeDate(LocalDate date);
boolean existsSprintPeriodWithGivenDate(LocalDate date);

/**
* Finds all sprints with given project that have end date after specified date and pages them
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import dev.corn.cornbackend.api.sprint.data.SprintRequest;
import dev.corn.cornbackend.api.sprint.data.SprintResponse;
import dev.corn.cornbackend.entities.backlog.item.interfaces.BacklogItemRepository;
import dev.corn.cornbackend.entities.project.interfaces.ProjectRepository;
import dev.corn.cornbackend.entities.sprint.Sprint;
import dev.corn.cornbackend.entities.sprint.interfaces.SprintMapper;
Expand Down Expand Up @@ -43,8 +44,13 @@ class SprintServiceTest {

@Mock
private SprintRepository sprintRepository;

@Mock
private ProjectRepository projectRepository;

@Mock
private BacklogItemRepository backlogItemRepository;

@Mock
private SprintMapper MAPPER;

Expand Down Expand Up @@ -447,7 +453,7 @@ final void test_updateSprintsStartDate_shouldThrowInvalidSprintDateException() {
User owner = ADD_SPRINT_DATA.project().getOwner();

// when
when(sprintRepository.existsEndDateBeforeDate(newStartDate))
when(sprintRepository.existsSprintPeriodWithGivenDate(newStartDate))
.thenReturn(true);

// then
Expand Down
4 changes: 4 additions & 0 deletions corn-frontend/src/app/app.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ import { provideAnimations } from '@angular/platform-browser/animations';
import { HTTP_INTERCEPTORS, provideHttpClient, withInterceptorsFromDi } from '@angular/common/http';
import { ErrorInterceptor } from '@core/interceptors/error.interceptor';
import { KeycloakAngularModule, KeycloakBearerInterceptor, KeycloakService } from 'keycloak-angular';
import { provideNativeDateAdapter } from "@angular/material/core";
import { DatePipe } from "@angular/common";

function initializeKeycloak(keycloak: KeycloakService) {
return () =>
Expand Down Expand Up @@ -50,5 +52,7 @@ export const appConfig: ApplicationConfig = {
deps: [KeycloakService]
},
KeycloakService,
provideNativeDateAdapter(),
DatePipe
]
};
9 changes: 8 additions & 1 deletion corn-frontend/src/app/core/enum/api-url.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,18 @@ export enum ApiUrl {

GET_SPRINTS_ON_PAGE = SPRINT_API_URL + '/getSprintsOnPage',
GET_CURRENT_AND_FUTURE_SPRINTS = SPRINT_API_URL + '/currentAndFuture',
DELETE_SPRINT = SPRINT_API_URL + '/deleteSprint',
UPDATE_SPRINTS_NAME = SPRINT_API_URL + '/updateSprintsName',
UPDATE_SPRINTS_START_DATE = SPRINT_API_URL + '/updateSprintsStartDate',
UPDATE_SPRINTS_END_DATE = SPRINT_API_URL + '/updateSprintsEndDate',
UPDATE_SPRINTS_DESCRIPTION = SPRINT_API_URL + '/updateSprintsDescription',
CREATE_SPRINT = SPRINT_API_URL + '/addSprint',

GET_PROJECT_MEMBERS = PROJECT_MEMBER_API_URL + '/getMembers',

GET_COMMENTS_FOR_BACKLOG_ITEM = BACKLOG_ITEM_COMMENT_API_RUL + '/getForItem',
UPDATE_COMMENT = BACKLOG_ITEM_COMMENT_API_RUL + '/update',
CREATE_COMMENT = BACKLOG_ITEM_COMMENT_API_RUL + '/add',
DELETE_COMMENT = BACKLOG_ITEM_COMMENT_API_RUL + '/delete'
DELETE_COMMENT = BACKLOG_ITEM_COMMENT_API_RUL + '/delete',

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
export interface SprintEditData {
sprintName: string;
goal: string;
startDate: string;
endDate: string;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
export interface SprintRequest {
sprintName: string;
description: string;
startDate: string;
endDate: string;
projectId: number;
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { Sprint } from "@interfaces/boards/backlog/sprint";
import { ApiUrl } from "@core/enum/api-url";
import { StorageService } from "@core/services/storage.service";
import { StorageKey } from "@core/enum/storage-key.enum";
import { SprintRequest } from "@interfaces/boards/backlog/sprint-request.interfaces";

@Injectable({
providedIn: 'root'
Expand All @@ -16,6 +17,52 @@ export class SprintService {

}

deleteSprint(sprintId: number): Observable<void> {
return this.http.delete<void>(ApiUrl.DELETE_SPRINT, {
params: {
sprintId: sprintId
}
});

}

editSprintName(name: string, sprintId: number): Observable<void> {
return this.http.put<void>(ApiUrl.UPDATE_SPRINTS_NAME, {}, {
params: {
name: name,
sprintId: sprintId
}
});
}

editSprintStartDate(startDate: string, sprintId: number): Observable<void> {
return this.http.put<void>(ApiUrl.UPDATE_SPRINTS_START_DATE, {}, {
params: {
startDate: startDate,
sprintId: sprintId
}
});
}

editSprintEndDate(endDate: string, sprintId: number): Observable<void> {
return this.http.put<void>(ApiUrl.UPDATE_SPRINTS_END_DATE, {}, {
params: {
endDate: endDate,
sprintId: sprintId
}
});
}

editSprintDescription(description: string, sprintId: number): Observable<void> {
return this.http.put<void>(ApiUrl.UPDATE_SPRINTS_DESCRIPTION, {}, {
params: {
description: description,
sprintId: sprintId
}
});

}

getSprintsOnPageForProject(pageNumber: number): Observable<Sprint[]> {
return this.http.get<Sprint[]>(ApiUrl.GET_SPRINTS_ON_PAGE, {
params: {
Expand All @@ -32,4 +79,8 @@ export class SprintService {
}
});
}

createSprint(result: SprintRequest): Observable<Sprint> {
return this.http.post<Sprint>(ApiUrl.CREATE_SPRINT, result);
}
}
Loading
Loading