Skip to content

Commit

Permalink
Integrated code lifecycle: Add filter and search to finished build jo…
Browse files Browse the repository at this point in the history
…bs (#8702)
  • Loading branch information
BBesrour authored Jun 21, 2024
1 parent 02a56b6 commit f2c2b9a
Show file tree
Hide file tree
Showing 16 changed files with 939 additions and 93 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

import static de.tum.in.www1.artemis.config.Constants.PROFILE_CORE;

import java.time.Duration;
import java.time.ZonedDateTime;
import java.util.List;
import java.util.Optional;
import java.util.Set;
Expand All @@ -17,6 +19,7 @@

import de.tum.in.www1.artemis.domain.BuildJob;
import de.tum.in.www1.artemis.domain.Result;
import de.tum.in.www1.artemis.domain.enumeration.BuildStatus;
import de.tum.in.www1.artemis.repository.base.ArtemisJpaRepository;
import de.tum.in.www1.artemis.service.connectors.localci.dto.DockerImageBuild;
import de.tum.in.www1.artemis.service.connectors.localci.dto.ResultBuildJob;
Expand All @@ -33,6 +36,36 @@ public interface BuildJobRepository extends ArtemisJpaRepository<BuildJob, Long>
@EntityGraph(attributePaths = { "result", "result.participation", "result.participation.exercise", "result.submission" })
Page<BuildJob> findAll(Pageable pageable);

// Cast to string is necessary. Otherwise, the query will fail on PostgreSQL.
@Query("""
SELECT b.id
FROM BuildJob b
LEFT JOIN Course c ON b.courseId = c.id
WHERE (:buildStatus IS NULL OR b.buildStatus = :buildStatus)
AND (:buildAgentAddress IS NULL OR b.buildAgentAddress = :buildAgentAddress)
AND (CAST(:startDate AS string) IS NULL OR b.buildStartDate >= :startDate)
AND (CAST(:endDate AS string) IS NULL OR b.buildStartDate <= :endDate)
AND (:searchTerm IS NULL OR (b.repositoryName LIKE %:searchTerm% OR c.title LIKE %:searchTerm%))
AND (:courseId IS NULL OR b.courseId = :courseId)
AND (:durationLower IS NULL OR (b.buildCompletionDate - b.buildStartDate) >= :durationLower)
AND (:durationUpper IS NULL OR (b.buildCompletionDate - b.buildStartDate) <= :durationUpper)
""")
Page<Long> findAllByFilterCriteria(@Param("buildStatus") BuildStatus buildStatus, @Param("buildAgentAddress") String buildAgentAddress,
@Param("startDate") ZonedDateTime startDate, @Param("endDate") ZonedDateTime endDate, @Param("searchTerm") String searchTerm, @Param("courseId") Long courseId,
@Param("durationLower") Duration durationLower, @Param("durationUpper") Duration durationUpper, Pageable pageable);

@Query("""
SELECT b
FROM BuildJob b
LEFT JOIN FETCH b.result r
LEFT JOIN FETCH r.participation p
LEFT JOIN FETCH p.exercise
LEFT JOIN FETCH r.submission
WHERE b.id IN :buildJobIds
""")
List<BuildJob> findAllByIdWithResults(@Param("buildJobIds") List<Long> buildJobIds);

@Query("""
SELECT new de.tum.in.www1.artemis.service.connectors.localci.dto.DockerImageBuild(
b.dockerImage,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import static de.tum.in.www1.artemis.config.Constants.PROFILE_LOCALCI;

import java.time.Duration;
import java.time.ZonedDateTime;
import java.util.ArrayList;
import java.util.List;
Expand All @@ -14,6 +15,8 @@
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Profile;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageImpl;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;

Expand All @@ -23,11 +26,14 @@
import com.hazelcast.map.IMap;
import com.hazelcast.topic.ITopic;

import de.tum.in.www1.artemis.domain.BuildJob;
import de.tum.in.www1.artemis.repository.BuildJobRepository;
import de.tum.in.www1.artemis.service.ProfileService;
import de.tum.in.www1.artemis.service.connectors.localci.dto.BuildAgentInformation;
import de.tum.in.www1.artemis.service.connectors.localci.dto.BuildJobQueueItem;
import de.tum.in.www1.artemis.service.connectors.localci.dto.DockerImageBuild;
import de.tum.in.www1.artemis.web.rest.dto.pageablesearch.FinishedBuildJobPageableSearchDTO;
import de.tum.in.www1.artemis.web.rest.util.PageUtil;

/**
* Includes methods for managing and retrieving the shared build job queue and build agent information. Also contains methods for cancelling build jobs.
Expand Down Expand Up @@ -279,4 +285,24 @@ public void cancelAllJobsForParticipation(long participationId) {
}
}

/**
* Get all finished build jobs that match the search criteria.
*
* @param search the search criteria
* @param courseId the id of the course
* @return the page of build jobs
*/
public Page<BuildJob> getFilteredFinishedBuildJobs(FinishedBuildJobPageableSearchDTO search, Long courseId) {
Duration buildDurationLower = search.buildDurationLower() == null ? null : Duration.ofSeconds(search.buildDurationLower());
Duration buildDurationUpper = search.buildDurationUpper() == null ? null : Duration.ofSeconds(search.buildDurationUpper());

Page<Long> buildJobIdsPage = buildJobRepository.findAllByFilterCriteria(search.buildStatus(), search.buildAgentAddress(), search.startDate(), search.endDate(),
search.pageable().getSearchTerm(), courseId, buildDurationLower, buildDurationUpper,
PageUtil.createDefaultPageRequest(search.pageable(), PageUtil.ColumnMapping.BUILD_JOB));

List<BuildJob> buildJobs = buildJobRepository.findAllByIdWithResults(buildJobIdsPage.toList());

return new PageImpl<>(buildJobs, buildJobIdsPage.getPageable(), buildJobIdsPage.getTotalElements());
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -20,14 +20,12 @@
import org.springframework.web.servlet.support.ServletUriComponentsBuilder;

import de.tum.in.www1.artemis.domain.BuildJob;
import de.tum.in.www1.artemis.repository.BuildJobRepository;
import de.tum.in.www1.artemis.security.annotations.EnforceAdmin;
import de.tum.in.www1.artemis.service.connectors.localci.SharedQueueManagementService;
import de.tum.in.www1.artemis.service.connectors.localci.dto.BuildAgentInformation;
import de.tum.in.www1.artemis.service.connectors.localci.dto.BuildJobQueueItem;
import de.tum.in.www1.artemis.service.dto.FinishedBuildJobDTO;
import de.tum.in.www1.artemis.web.rest.dto.pageablesearch.PageableSearchDTO;
import de.tum.in.www1.artemis.web.rest.util.PageUtil;
import de.tum.in.www1.artemis.web.rest.dto.pageablesearch.FinishedBuildJobPageableSearchDTO;
import tech.jhipster.web.util.PaginationUtil;

@Profile(PROFILE_LOCALCI)
Expand All @@ -37,13 +35,10 @@ public class AdminBuildJobQueueResource {

private final SharedQueueManagementService localCIBuildJobQueueService;

private final BuildJobRepository buildJobRepository;

private static final Logger log = LoggerFactory.getLogger(AdminBuildJobQueueResource.class);

public AdminBuildJobQueueResource(SharedQueueManagementService localCIBuildJobQueueService, BuildJobRepository buildJobRepository) {
public AdminBuildJobQueueResource(SharedQueueManagementService localCIBuildJobQueueService) {
this.localCIBuildJobQueueService = localCIBuildJobQueueService;
this.buildJobRepository = buildJobRepository;
}

/**
Expand Down Expand Up @@ -170,11 +165,14 @@ public ResponseEntity<Void> cancelAllRunningBuildJobsForAgent(@RequestParam Stri
*/
@GetMapping("finished-jobs")
@EnforceAdmin
public ResponseEntity<List<FinishedBuildJobDTO>> getFinishedBuildJobs(PageableSearchDTO<String> search) {
log.debug("REST request to get a page of finished build jobs");
final Page<BuildJob> page = buildJobRepository.findAll(PageUtil.createDefaultPageRequest(search, PageUtil.ColumnMapping.BUILD_JOB));
Page<FinishedBuildJobDTO> finishedBuildJobDTOs = FinishedBuildJobDTO.fromBuildJobsPage(page);
HttpHeaders headers = PaginationUtil.generatePaginationHttpHeaders(ServletUriComponentsBuilder.fromCurrentRequest(), page);
public ResponseEntity<List<FinishedBuildJobDTO>> getFinishedBuildJobs(FinishedBuildJobPageableSearchDTO search) {
log.debug("REST request to get a page of finished build jobs with build status {}, build agent address {}, start date {} and end date {}", search.buildStatus(),
search.buildAgentAddress(), search.startDate(), search.endDate());

Page<BuildJob> buildJobPage = localCIBuildJobQueueService.getFilteredFinishedBuildJobs(search, null);

Page<FinishedBuildJobDTO> finishedBuildJobDTOs = FinishedBuildJobDTO.fromBuildJobsPage(buildJobPage);
HttpHeaders headers = PaginationUtil.generatePaginationHttpHeaders(ServletUriComponentsBuilder.fromCurrentRequest(), buildJobPage);
return new ResponseEntity<>(finishedBuildJobDTOs.getContent(), headers, HttpStatus.OK);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package de.tum.in.www1.artemis.web.rest.dto.pageablesearch;

import java.time.ZonedDateTime;

import com.fasterxml.jackson.annotation.JsonInclude;

import de.tum.in.www1.artemis.domain.enumeration.BuildStatus;

@JsonInclude(JsonInclude.Include.NON_EMPTY)
public record FinishedBuildJobPageableSearchDTO(BuildStatus buildStatus, String buildAgentAddress, ZonedDateTime startDate, ZonedDateTime endDate, Integer buildDurationLower,
Integer buildDurationUpper, SearchTermPageableSearchDTO<String> pageable) {
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,17 +20,15 @@

import de.tum.in.www1.artemis.domain.BuildJob;
import de.tum.in.www1.artemis.domain.Course;
import de.tum.in.www1.artemis.repository.BuildJobRepository;
import de.tum.in.www1.artemis.repository.CourseRepository;
import de.tum.in.www1.artemis.security.annotations.EnforceAtLeastInstructor;
import de.tum.in.www1.artemis.security.annotations.enforceRoleInCourse.EnforceAtLeastInstructorInCourse;
import de.tum.in.www1.artemis.service.AuthorizationCheckService;
import de.tum.in.www1.artemis.service.connectors.localci.SharedQueueManagementService;
import de.tum.in.www1.artemis.service.connectors.localci.dto.BuildJobQueueItem;
import de.tum.in.www1.artemis.service.dto.FinishedBuildJobDTO;
import de.tum.in.www1.artemis.web.rest.dto.pageablesearch.PageableSearchDTO;
import de.tum.in.www1.artemis.web.rest.dto.pageablesearch.FinishedBuildJobPageableSearchDTO;
import de.tum.in.www1.artemis.web.rest.errors.AccessForbiddenException;
import de.tum.in.www1.artemis.web.rest.util.PageUtil;
import tech.jhipster.web.util.PaginationUtil;

@Profile(PROFILE_LOCALCI)
Expand All @@ -46,14 +44,10 @@ public class BuildJobQueueResource {

private final CourseRepository courseRepository;

private final BuildJobRepository buildJobRepository;

public BuildJobQueueResource(SharedQueueManagementService localCIBuildJobQueueService, AuthorizationCheckService authorizationCheckService, CourseRepository courseRepository,
BuildJobRepository buildJobRepository) {
public BuildJobQueueResource(SharedQueueManagementService localCIBuildJobQueueService, AuthorizationCheckService authorizationCheckService, CourseRepository courseRepository) {
this.localCIBuildJobQueueService = localCIBuildJobQueueService;
this.authorizationCheckService = authorizationCheckService;
this.courseRepository = courseRepository;
this.buildJobRepository = buildJobRepository;
}

/**
Expand Down Expand Up @@ -163,11 +157,11 @@ public ResponseEntity<Void> cancelAllRunningBuildJobs(@PathVariable long courseI
*/
@GetMapping("courses/{courseId}/finished-jobs")
@EnforceAtLeastInstructorInCourse
public ResponseEntity<List<FinishedBuildJobDTO>> getFinishedBuildJobsForCourse(@PathVariable long courseId, PageableSearchDTO<String> search) {
public ResponseEntity<List<FinishedBuildJobDTO>> getFinishedBuildJobsForCourse(@PathVariable long courseId, FinishedBuildJobPageableSearchDTO search) {
log.debug("REST request to get the finished build jobs for course {}", courseId);
final Page<BuildJob> page = buildJobRepository.findAllByCourseId(courseId, PageUtil.createDefaultPageRequest(search, PageUtil.ColumnMapping.BUILD_JOB));
Page<FinishedBuildJobDTO> finishedBuildJobDTOs = FinishedBuildJobDTO.fromBuildJobsPage(page);
HttpHeaders headers = PaginationUtil.generatePaginationHttpHeaders(ServletUriComponentsBuilder.fromCurrentRequest(), page);
Page<BuildJob> buildJobPage = localCIBuildJobQueueService.getFilteredFinishedBuildJobs(search, courseId);
Page<FinishedBuildJobDTO> finishedBuildJobDTOs = FinishedBuildJobDTO.fromBuildJobsPage(buildJobPage);
HttpHeaders headers = PaginationUtil.generatePaginationHttpHeaders(ServletUriComponentsBuilder.fromCurrentRequest(), buildJobPage);
return new ResponseEntity<>(finishedBuildJobDTOs.getContent(), headers, HttpStatus.OK);
}

Expand Down
Loading

0 comments on commit f2c2b9a

Please sign in to comment.