diff options
author | Teryk Bellahsene <teryk.bellahsene@sonarsource.com> | 2016-02-11 21:20:18 +0100 |
---|---|---|
committer | Teryk Bellahsene <teryk.bellahsene@sonarsource.com> | 2016-02-15 13:44:59 +0100 |
commit | 89a93e795af89c98f5e99de60620c21287f7889e (patch) | |
tree | 105152b4d0ac83e11cdc6749b2e37c8b3b24e338 | |
parent | 8909ccd99b9bc5874f24395519d66e2bcb9bdacb (diff) | |
download | sonarqube-89a93e795af89c98f5e99de60620c21287f7889e.tar.gz sonarqube-89a93e795af89c98f5e99de60620c21287f7889e.zip |
SONAR-7187 WS api/ce/activity handles queue and past CE tasks
21 files changed, 727 insertions, 143 deletions
diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/ws/ActivityAction.java b/server/sonar-server/src/main/java/org/sonar/server/computation/ws/ActivityAction.java index 5189cae7f57..fca135da732 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/computation/ws/ActivityAction.java +++ b/server/sonar-server/src/main/java/org/sonar/server/computation/ws/ActivityAction.java @@ -17,19 +17,23 @@ * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ + package org.sonar.server.computation.ws; +import com.google.common.base.Joiner; +import com.google.common.collect.ImmutableList; import com.google.common.collect.Lists; import java.util.Date; +import java.util.LinkedHashSet; import java.util.List; -import javax.annotation.CheckForNull; -import javax.annotation.Nullable; -import org.apache.ibatis.session.RowBounds; +import java.util.Set; import org.sonar.api.resources.Qualifiers; import org.sonar.api.server.ws.Request; import org.sonar.api.server.ws.Response; import org.sonar.api.server.ws.WebService; +import org.sonar.api.server.ws.WebService.Param; import org.sonar.api.utils.DateUtils; +import org.sonar.api.utils.Paging; import org.sonar.api.web.UserRole; import org.sonar.core.permission.GlobalPermissions; import org.sonar.core.util.Uuids; @@ -37,124 +41,141 @@ import org.sonar.db.DbClient; import org.sonar.db.DbSession; import org.sonar.db.ce.CeActivityDto; import org.sonar.db.ce.CeActivityQuery; +import org.sonar.db.ce.CeQueueDto; import org.sonar.db.ce.CeTaskTypes; import org.sonar.db.component.ComponentDto; import org.sonar.db.component.ComponentDtoFunctions; import org.sonar.db.component.ComponentQuery; -import org.sonar.server.exceptions.BadRequestException; +import org.sonar.server.computation.taskprocessor.CeTaskProcessor; import org.sonar.server.exceptions.ForbiddenException; import org.sonar.server.user.UserSession; -import org.sonar.server.ws.WsUtils; import org.sonarqube.ws.Common; import org.sonarqube.ws.WsCe; +import org.sonarqube.ws.WsCe.ActivityResponse; +import org.sonarqube.ws.client.ce.ActivityWsRequest; import static java.lang.String.format; +import static org.sonar.api.utils.DateUtils.dateToLong; +import static org.sonar.api.utils.DateUtils.parseDateTimeQuietly; +import static org.sonar.api.utils.Paging.offset; import static org.sonar.server.ws.WsUtils.checkRequest; +import static org.sonar.server.ws.WsUtils.writeProtobuf; +import static org.sonarqube.ws.client.ce.CeWsParameters.PARAM_COMPONENT_ID; +import static org.sonarqube.ws.client.ce.CeWsParameters.PARAM_COMPONENT_QUERY; +import static org.sonarqube.ws.client.ce.CeWsParameters.PARAM_MAX_EXECUTED_AT; +import static org.sonarqube.ws.client.ce.CeWsParameters.PARAM_MIN_SUBMITTED_AT; +import static org.sonarqube.ws.client.ce.CeWsParameters.PARAM_ONLY_CURRENTS; +import static org.sonarqube.ws.client.ce.CeWsParameters.PARAM_STATUS; +import static org.sonarqube.ws.client.ce.CeWsParameters.PARAM_TYPE; public class ActivityAction implements CeWsAction { - - private static final String PARAM_COMPONENT_UUID = "componentId"; - private static final String PARAM_COMPONENT_QUERY = "componentQuery"; - private static final String PARAM_TYPE = "type"; - private static final String PARAM_STATUS = "status"; - private static final String PARAM_ONLY_CURRENTS = "onlyCurrents"; - private static final String PARAM_MIN_SUBMITTED_AT = "minSubmittedAt"; - private static final String PARAM_MAX_EXECUTED_AT = "maxExecutedAt"; - private final UserSession userSession; private final DbClient dbClient; private final TaskFormatter formatter; + private final Set<String> taskTypes; - public ActivityAction(UserSession userSession, DbClient dbClient, TaskFormatter formatter) { + public ActivityAction(UserSession userSession, DbClient dbClient, TaskFormatter formatter, CeTaskProcessor[] taskProcessors) { this.userSession = userSession; this.dbClient = dbClient; this.formatter = formatter; + + this.taskTypes = new LinkedHashSet<>(); + for (CeTaskProcessor taskProcessor : taskProcessors) { + taskTypes.addAll(taskProcessor.getHandledCeTaskTypes()); + } } @Override public void define(WebService.NewController controller) { WebService.NewAction action = controller.createAction("activity") - .setDescription(format("Search for past task executions. Requires the system administration permission, " + - "or project administration permission if %s is set.", PARAM_COMPONENT_UUID)) + .setDescription(format("Search for tasks.<br> " + + "Requires the system administration permission, " + + "or project administration permission if %s is set.", PARAM_COMPONENT_ID)) .setResponseExample(getClass().getResource("activity-example.json")) .setHandler(this) .setSince("5.2"); - action.createParam(PARAM_COMPONENT_UUID) - .setDescription("Optional id of the component (project) to filter on") + + action.createParam(PARAM_COMPONENT_ID) + .setDescription("Id of the component (project) to filter on") .setExampleValue(Uuids.UUID_EXAMPLE_03); action.createParam(PARAM_COMPONENT_QUERY) - .setDescription(format("Optional search by component name or key. Must not be set together with %s.", PARAM_COMPONENT_UUID)) + .setDescription(format("Limit search to: <ul>" + + "<li>component names that contain the supplied string</li>" + + "<li>component keys that are exactly the same as the supplied string</li>" + + "</ul>" + + "Must not be set together with %s", PARAM_COMPONENT_ID)) .setExampleValue("Apache"); action.createParam(PARAM_STATUS) - .setDescription("Optional filter on task status") - .setPossibleValues(CeActivityDto.Status.values()); + .setDescription("Comma separated list of task statuses") + .setPossibleValues(ImmutableList.builder() + .add(CeActivityDto.Status.values()) + .add(CeQueueDto.Status.values()).build()) + .setExampleValue(Joiner.on(",").join(CeQueueDto.Status.IN_PROGRESS, CeActivityDto.Status.SUCCESS)) + // activity statuses by default to be backward compatible + // queued tasks have been added in 5.5 + .setDefaultValue(Joiner.on(",").join(CeActivityDto.Status.values())); action.createParam(PARAM_ONLY_CURRENTS) - .setDescription("Optional filter on the current activities (only the most recent task by project)") + .setDescription("Filter on the last tasks (only the most recent finished task by project)") .setBooleanPossibleValues() .setDefaultValue("false"); action.createParam(PARAM_TYPE) - .setDescription("Optional filter on task type") - .setExampleValue(CeTaskTypes.REPORT); + .setDescription("Task type") + .setExampleValue(CeTaskTypes.REPORT) + .setPossibleValues(taskTypes); action.createParam(PARAM_MIN_SUBMITTED_AT) - .setDescription("Optional filter on minimum date of task submission") + .setDescription("Minimum date of task submission") .setExampleValue(DateUtils.formatDateTime(new Date())); action.createParam(PARAM_MAX_EXECUTED_AT) - .setDescription("Optional filter on the maximum date of end of task processing") + .setDescription("Maximum date of end of task processing") .setExampleValue(DateUtils.formatDateTime(new Date())); - action.addPagingParams(10); + action.addPagingParams(100); } @Override public void handle(Request wsRequest, Response wsResponse) throws Exception { + ActivityResponse activityResponse = doHandle(toSearchWsRequest(wsRequest)); + writeProtobuf(activityResponse, wsRequest, wsResponse); + } + + private ActivityResponse doHandle(ActivityWsRequest request) { DbSession dbSession = dbClient.openSession(false); try { - CeActivityQuery query = buildQuery(dbSession, wsRequest); + CeActivityQuery query = buildQuery(dbSession, request); checkPermissions(query); + TaskResult queuedTasks = loadQueuedTasks(dbSession, request, query); + TaskResult pastTasks = loadPastTasks(dbSession, request, query, queuedTasks.total); - RowBounds rowBounds = readMyBatisRowBounds(wsRequest); - List<CeActivityDto> dtos = dbClient.ceActivityDao().selectByQuery(dbSession, query, rowBounds); - int total = dbClient.ceActivityDao().countByQuery(dbSession, query); - - WsCe.ActivityResponse.Builder wsResponseBuilder = WsCe.ActivityResponse.newBuilder(); - wsResponseBuilder.addAllTasks(formatter.formatActivity(dbSession, dtos)); - wsResponseBuilder.setPaging(Common.Paging.newBuilder() - .setPageIndex(wsRequest.mandatoryParamAsInt(WebService.Param.PAGE)) - .setPageSize(wsRequest.mandatoryParamAsInt(WebService.Param.PAGE_SIZE)) - .setTotal(total)); - WsUtils.writeProtobuf(wsResponseBuilder.build(), wsRequest, wsResponse); + return buildResponse( + queuedTasks.tasks, + pastTasks.tasks, + Paging.forPageIndex(request.getPage()) + .withPageSize(request.getPageSize()) + .andTotal(queuedTasks.total + pastTasks.total)); } finally { dbClient.closeSession(dbSession); } } - private CeActivityQuery buildQuery(DbSession dbSession, Request wsRequest) { - String componentUuid = wsRequest.param(PARAM_COMPONENT_UUID); - String componentQuery = wsRequest.param(PARAM_COMPONENT_QUERY); - checkRequest(componentUuid == null || componentQuery == null, - format("Only one of following parameters is accepted: %s or %s", PARAM_COMPONENT_UUID, PARAM_COMPONENT_QUERY)); - + private CeActivityQuery buildQuery(DbSession dbSession, ActivityWsRequest request) { CeActivityQuery query = new CeActivityQuery(); - query.setType(wsRequest.param(PARAM_TYPE)); - query.setOnlyCurrents(wsRequest.mandatoryParamAsBoolean(PARAM_ONLY_CURRENTS)); - query.setMinSubmittedAt(toTime(wsRequest.paramAsDateTime(PARAM_MIN_SUBMITTED_AT))); - query.setMaxExecutedAt(toTime(wsRequest.paramAsDateTime(PARAM_MAX_EXECUTED_AT))); - - String status = wsRequest.param(PARAM_STATUS); - if (status != null) { - query.setStatus(CeActivityDto.Status.valueOf(status)); + query.setType(request.getType()); + query.setOnlyCurrents(request.getOnlyCurrents()); + query.setMinSubmittedAt(dateToLong(parseDateTimeQuietly(request.getMinSubmittedAt()))); + query.setMaxExecutedAt(dateToLong(parseDateTimeQuietly(request.getMaxExecutedAt()))); + + List<String> statuses = request.getStatus(); + if (statuses != null && !statuses.isEmpty()) { + query.setStatuses(request.getStatus()); } - loadComponentUuids(dbSession, wsRequest, query); + loadComponentUuids(dbSession, request, query); return query; } - private void loadComponentUuids(DbSession dbSession, Request wsRequest, CeActivityQuery query) { - String componentUuid = wsRequest.param(PARAM_COMPONENT_UUID); - String componentQuery = wsRequest.param(PARAM_COMPONENT_QUERY); - if (componentUuid != null && componentQuery != null) { - throw new BadRequestException(format("Only one of parameters must be set: %s or %s", PARAM_COMPONENT_UUID, PARAM_COMPONENT_QUERY)); - } + private void loadComponentUuids(DbSession dbSession, ActivityWsRequest request, CeActivityQuery query) { + String componentUuid = request.getComponentId(); + String componentQuery = request.getComponentQuery(); if (componentUuid != null) { query.setComponentUuid(componentUuid); @@ -166,6 +187,26 @@ public class ActivityAction implements CeWsAction { } } + private TaskResult loadQueuedTasks(DbSession dbSession, ActivityWsRequest request, CeActivityQuery query) { + int total = dbClient.ceQueueDao().countByQuery(dbSession, query); + List<CeQueueDto> dtos = dbClient.ceQueueDao().selectByQueryInDescOrder(dbSession, query, + Paging.forPageIndex(request.getPage()) + .withPageSize(request.getPageSize()) + .andTotal(total)); + Iterable<WsCe.Task> tasks = formatter.formatQueue(dbSession, dtos); + return new TaskResult(tasks, total); + } + + private TaskResult loadPastTasks(DbSession dbSession, ActivityWsRequest request, CeActivityQuery query, int totalQueuedTasks) { + int total = dbClient.ceActivityDao().countByQuery(dbSession, query); + // we have to take into account the total number of queue tasks found + int offset = Math.max(0, offset(request.getPage(), request.getPageSize()) - totalQueuedTasks); + List<CeActivityDto> dtos = dbClient.ceActivityDao().selectByQuery(dbSession, query, offset, request.getPageSize()); + Iterable<WsCe.Task> ceTasks = formatter.formatActivity(dbSession, dtos); + + return new TaskResult(ceTasks, total); + } + private void checkPermissions(CeActivityQuery query) { List<String> componentUuids = query.getComponentUuids(); if (componentUuids != null && componentUuids.size() == 1) { @@ -177,18 +218,61 @@ public class ActivityAction implements CeWsAction { } } - private static RowBounds readMyBatisRowBounds(Request wsRequest) { - int pageIndex = wsRequest.mandatoryParamAsInt(WebService.Param.PAGE); - int pageSize = wsRequest.mandatoryParamAsInt(WebService.Param.PAGE_SIZE); - return new RowBounds((pageIndex - 1) * pageSize, pageSize); + public static boolean isAllowedOnComponentUuid(UserSession userSession, String componentUuid) { + return userSession.hasPermission(GlobalPermissions.SYSTEM_ADMIN) || userSession.hasComponentUuidPermission(UserRole.ADMIN, componentUuid); } - @CheckForNull - private static Long toTime(@Nullable Date date) { - return date == null ? null : date.getTime(); + private static ActivityResponse buildResponse(Iterable<WsCe.Task> queuedTasks, Iterable<WsCe.Task> pastTasks, Paging paging) { + WsCe.ActivityResponse.Builder wsResponseBuilder = WsCe.ActivityResponse.newBuilder(); + + int nbInsertedTasks = 0; + for (WsCe.Task queuedTask : queuedTasks) { + if (nbInsertedTasks < paging.pageSize()) { + wsResponseBuilder.addTasks(queuedTask); + nbInsertedTasks++; + } + } + + for (WsCe.Task pastTask : pastTasks) { + if (nbInsertedTasks < paging.pageSize()) { + wsResponseBuilder.addTasks(pastTask); + nbInsertedTasks++; + } + } + + wsResponseBuilder.setPaging(Common.Paging.newBuilder() + .setPageIndex(paging.pageIndex()) + .setPageSize(paging.pageSize()) + .setTotal(paging.total())); + + return wsResponseBuilder.build(); } - public static boolean isAllowedOnComponentUuid(UserSession userSession, String componentUuid) { - return userSession.hasPermission(GlobalPermissions.SYSTEM_ADMIN) || userSession.hasComponentUuidPermission(UserRole.ADMIN, componentUuid); + private static ActivityWsRequest toSearchWsRequest(Request request) { + ActivityWsRequest activityWsRequest = new ActivityWsRequest() + .setComponentId(request.param(PARAM_COMPONENT_ID)) + .setComponentQuery(request.param(PARAM_COMPONENT_QUERY)) + .setStatus(request.paramAsStrings(PARAM_STATUS)) + .setType(request.param(PARAM_TYPE)) + .setMaxExecutedAt(request.param(PARAM_MAX_EXECUTED_AT)) + .setMinSubmittedAt(request.param(PARAM_MIN_SUBMITTED_AT)) + .setOnlyCurrents(request.paramAsBoolean(PARAM_ONLY_CURRENTS)) + .setPage(request.mandatoryParamAsInt(Param.PAGE)) + .setPageSize(request.mandatoryParamAsInt(Param.PAGE_SIZE)); + + checkRequest(activityWsRequest.getComponentId() == null || activityWsRequest.getComponentQuery() == null, "%s and %s must not be set at the same time", + PARAM_COMPONENT_ID, PARAM_COMPONENT_QUERY); + + return activityWsRequest; + } + + private static class TaskResult { + private final Iterable<WsCe.Task> tasks; + private final int total; + + private TaskResult(Iterable<WsCe.Task> tasks, int total) { + this.tasks = tasks; + this.total = total; + } } } diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/ws/CeWsModule.java b/server/sonar-server/src/main/java/org/sonar/server/computation/ws/CeWsModule.java index dd29f1297e7..edc04a1202e 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/computation/ws/CeWsModule.java +++ b/server/sonar-server/src/main/java/org/sonar/server/computation/ws/CeWsModule.java @@ -26,7 +26,6 @@ public class CeWsModule extends Module { protected void configureModule() { add( CeWs.class, - ActivityAction.class, CancelAction.class, CancelAllAction.class, QueueAction.class, @@ -35,6 +34,7 @@ public class CeWsModule extends Module { ComponentAction.class, SubmitAction.class, TaskFormatter.class, - TaskAction.class); + TaskAction.class, + ActivityAction.class); } } diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/ws/ComponentAction.java b/server/sonar-server/src/main/java/org/sonar/server/computation/ws/ComponentAction.java index ec7a0c80f08..1a30756dc7e 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/computation/ws/ComponentAction.java +++ b/server/sonar-server/src/main/java/org/sonar/server/computation/ws/ComponentAction.java @@ -20,7 +20,6 @@ package org.sonar.server.computation.ws; import java.util.List; -import org.apache.ibatis.session.RowBounds; import org.sonar.api.server.ws.Request; import org.sonar.api.server.ws.Response; import org.sonar.api.server.ws.WebService; @@ -75,7 +74,7 @@ public class ComponentAction implements CeWsAction { CeActivityQuery activityQuery = new CeActivityQuery() .setComponentUuid(componentUuid) .setOnlyCurrents(true); - List<CeActivityDto> activityDtos = dbClient.ceActivityDao().selectByQuery(dbSession, activityQuery, new RowBounds(0, 1)); + List<CeActivityDto> activityDtos = dbClient.ceActivityDao().selectByQuery(dbSession, activityQuery, 0, 1); ProjectResponse.Builder wsResponseBuilder = ProjectResponse.newBuilder(); wsResponseBuilder.addAllQueue(formatter.formatQueue(dbSession, queueDtos)); diff --git a/server/sonar-server/src/test/java/org/sonar/server/computation/ws/ActivityActionTest.java b/server/sonar-server/src/test/java/org/sonar/server/computation/ws/ActivityActionTest.java index d491bc46957..967c93dcb75 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/computation/ws/ActivityActionTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/computation/ws/ActivityActionTest.java @@ -17,6 +17,7 @@ * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ + package org.sonar.server.computation.ws; import com.google.common.base.Optional; @@ -39,12 +40,13 @@ import org.sonar.db.ce.CeTaskTypes; import org.sonar.db.component.ComponentDbTester; import org.sonar.server.computation.log.CeLogging; import org.sonar.server.computation.log.LogFileRef; +import org.sonar.server.computation.taskprocessor.CeTaskProcessor; import org.sonar.server.exceptions.BadRequestException; -import org.sonarqube.ws.MediaTypes; import org.sonar.server.tester.UserSessionRule; import org.sonar.server.ws.TestResponse; import org.sonar.server.ws.WsActionTester; import org.sonar.test.JsonAssert; +import org.sonarqube.ws.MediaTypes; import org.sonarqube.ws.WsCe; import static java.util.Arrays.asList; @@ -53,6 +55,7 @@ import static org.mockito.Matchers.any; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; import static org.sonar.db.component.ComponentTesting.newProjectDto; +import static org.sonarqube.ws.client.ce.CeWsParameters.PARAM_STATUS; public class ActivityActionTest { @@ -69,8 +72,8 @@ public class ActivityActionTest { CeLogging ceLogging = mock(CeLogging.class); TaskFormatter formatter = new TaskFormatter(dbTester.getDbClient(), ceLogging, System2.INSTANCE); - ActivityAction underTest = new ActivityAction(userSession, dbTester.getDbClient(), formatter); - WsActionTester tester = new WsActionTester(underTest); + ActivityAction underTest = new ActivityAction(userSession, dbTester.getDbClient(), formatter, new CeTaskProcessor[] {mock(CeTaskProcessor.class)}); + WsActionTester ws = new WsActionTester(underTest); @Before public void setUp() { @@ -80,15 +83,15 @@ public class ActivityActionTest { @Test public void get_all_past_activity() { userSession.setGlobalPermissions(UserRole.ADMIN); - insert("T1", "PROJECT_1", CeActivityDto.Status.SUCCESS); - insert("T2", "PROJECT_2", CeActivityDto.Status.FAILED); + insertActivity("T1", "PROJECT_1", CeActivityDto.Status.SUCCESS); + insertActivity("T2", "PROJECT_2", CeActivityDto.Status.FAILED); - TestResponse wsResponse = tester.newRequest() + TestResponse wsResponse = ws.newRequest() .setMediaType(MediaTypes.PROTOBUF) .execute(); // verify the protobuf response - WsCe.ActivityResponse activityResponse = Protobuf.read(wsResponse.getInputStream(), WsCe.ActivityResponse.PARSER); + WsCe.ActivityResponse activityResponse = Protobuf.read(wsResponse.getInputStream(), WsCe.ActivityResponse.parser()); assertThat(activityResponse.getTasksCount()).isEqualTo(2); // chronological order, from newest to oldest @@ -107,32 +110,35 @@ public class ActivityActionTest { @Test public void filter_by_status() { userSession.setGlobalPermissions(UserRole.ADMIN); - insert("T1", "PROJECT_1", CeActivityDto.Status.SUCCESS); - insert("T2", "PROJECT_2", CeActivityDto.Status.FAILED); + insertActivity("T1", "PROJECT_1", CeActivityDto.Status.SUCCESS); + insertActivity("T2", "PROJECT_2", CeActivityDto.Status.FAILED); + insertQueue("T3", "PROJECT_1", CeQueueDto.Status.IN_PROGRESS); - TestResponse wsResponse = tester.newRequest() - .setParam("status", "FAILED") + TestResponse wsResponse = ws.newRequest() + .setParam("status", "FAILED,IN_PROGRESS") .setMediaType(MediaTypes.PROTOBUF) .execute(); - WsCe.ActivityResponse activityResponse = Protobuf.read(wsResponse.getInputStream(), WsCe.ActivityResponse.PARSER); - assertThat(activityResponse.getTasksCount()).isEqualTo(1); - assertThat(activityResponse.getTasks(0).getId()).isEqualTo("T2"); + WsCe.ActivityResponse activityResponse = Protobuf.read(wsResponse.getInputStream(), WsCe.ActivityResponse.parser()); + assertThat(activityResponse.getTasksCount()).isEqualTo(2); + assertThat(activityResponse.getTasks(0).getId()).isEqualTo("T3"); + assertThat(activityResponse.getTasks(1).getId()).isEqualTo("T2"); } @Test public void filter_on_current_activities() { userSession.setGlobalPermissions(UserRole.ADMIN); // T2 is the current activity (the most recent one) - insert("T1", "PROJECT_1", CeActivityDto.Status.SUCCESS); - insert("T2", "PROJECT_1", CeActivityDto.Status.FAILED); + insertActivity("T1", "PROJECT_1", CeActivityDto.Status.SUCCESS); + insertActivity("T2", "PROJECT_1", CeActivityDto.Status.FAILED); + insertQueue("T3", "PROJECT_1", CeQueueDto.Status.PENDING); - TestResponse wsResponse = tester.newRequest() + TestResponse wsResponse = ws.newRequest() .setParam("onlyCurrents", "true") .setMediaType(MediaTypes.PROTOBUF) .execute(); - WsCe.ActivityResponse activityResponse = Protobuf.read(wsResponse.getInputStream(), WsCe.ActivityResponse.PARSER); + WsCe.ActivityResponse activityResponse = Protobuf.read(wsResponse.getInputStream(), WsCe.ActivityResponse.parser()); assertThat(activityResponse.getTasksCount()).isEqualTo(1); assertThat(activityResponse.getTasks(0).getId()).isEqualTo("T2"); } @@ -140,23 +146,25 @@ public class ActivityActionTest { @Test public void paginate_results() { userSession.setGlobalPermissions(UserRole.ADMIN); - insert("T1", "PROJECT_1", CeActivityDto.Status.SUCCESS); - insert("T2", "PROJECT_2", CeActivityDto.Status.FAILED); - - assertPage(1, 1, 2, asList("T2")); - assertPage(2, 1, 2, asList("T1")); - assertPage(1, 10, 2, asList("T2", "T1")); - assertPage(2, 10, 2, Collections.<String>emptyList()); + insertActivity("T1", "PROJECT_1", CeActivityDto.Status.SUCCESS); + insertActivity("T2", "PROJECT_2", CeActivityDto.Status.FAILED); + insertQueue("T3", "PROJECT_1", CeQueueDto.Status.IN_PROGRESS); + + assertPage(1, 1, 3, asList("T3")); + assertPage(2, 1, 3, asList("T2")); + assertPage(1, 10, 3, asList("T3", "T2", "T1")); + assertPage(2, 10, 3, Collections.<String>emptyList()); } private void assertPage(int pageIndex, int pageSize, int expectedTotal, List<String> expectedOrderedTaskIds) { - TestResponse wsResponse = tester.newRequest() + TestResponse wsResponse = ws.newRequest() .setMediaType(MediaTypes.PROTOBUF) .setParam(WebService.Param.PAGE, Integer.toString(pageIndex)) .setParam(WebService.Param.PAGE_SIZE, Integer.toString(pageSize)) + .setParam(PARAM_STATUS, "SUCCESS,FAILED,CANCELED,IN_PROGRESS,PENDING") .execute(); - WsCe.ActivityResponse activityResponse = Protobuf.read(wsResponse.getInputStream(), WsCe.ActivityResponse.PARSER); + WsCe.ActivityResponse activityResponse = Protobuf.read(wsResponse.getInputStream(), WsCe.ActivityResponse.parser()); assertThat(activityResponse.getPaging().getPageIndex()).isEqualTo(pageIndex); assertThat(activityResponse.getPaging().getPageSize()).isEqualTo(pageSize); assertThat(activityResponse.getPaging().getTotal()).isEqualTo(expectedTotal); @@ -172,15 +180,15 @@ public class ActivityActionTest { public void project_administrator_can_access_his_project_activity() { // no need to be a system admin userSession.addComponentUuidPermission(UserRole.ADMIN, "PROJECT_1", "PROJECT_1"); - insert("T1", "PROJECT_1", CeActivityDto.Status.SUCCESS); - insert("T2", "PROJECT_2", CeActivityDto.Status.FAILED); + insertActivity("T1", "PROJECT_1", CeActivityDto.Status.SUCCESS); + insertActivity("T2", "PROJECT_2", CeActivityDto.Status.FAILED); - TestResponse wsResponse = tester.newRequest() + TestResponse wsResponse = ws.newRequest() .setParam("componentId", "PROJECT_1") .setMediaType(MediaTypes.PROTOBUF) .execute(); - WsCe.ActivityResponse activityResponse = Protobuf.read(wsResponse.getInputStream(), WsCe.ActivityResponse.PARSER); + WsCe.ActivityResponse activityResponse = Protobuf.read(wsResponse.getInputStream(), WsCe.ActivityResponse.parser()); assertThat(activityResponse.getTasksCount()).isEqualTo(1); assertThat(activityResponse.getTasks(0).getId()).isEqualTo("T1"); assertThat(activityResponse.getTasks(0).getStatus()).isEqualTo(WsCe.TaskStatus.SUCCESS); @@ -195,11 +203,11 @@ public class ActivityActionTest { dbTester.commit(); componentDb.indexProjects(); userSession.setGlobalPermissions(UserRole.ADMIN); - insert("T1", "P1", CeActivityDto.Status.SUCCESS); - insert("T2", "P2", CeActivityDto.Status.SUCCESS); - insert("T3", "P3", CeActivityDto.Status.SUCCESS); + insertActivity("T1", "P1", CeActivityDto.Status.SUCCESS); + insertActivity("T2", "P2", CeActivityDto.Status.SUCCESS); + insertActivity("T3", "P3", CeActivityDto.Status.SUCCESS); - TestResponse wsResponse = tester.newRequest() + TestResponse wsResponse = ws.newRequest() .setParam("componentQuery", "apac") .setMediaType(MediaTypes.PROTOBUF) .execute(); @@ -211,9 +219,9 @@ public class ActivityActionTest { @Test public void fail_if_both_filters_on_component_id_and_name() { expectedException.expect(BadRequestException.class); - expectedException.expectMessage("Only one of following parameters is accepted: componentId or componentQuery"); + expectedException.expectMessage("componentId and componentQuery must not be set at the same time"); - tester.newRequest() + ws.newRequest() .setParam("componentId", "ID1") .setParam("componentQuery", "apache") .setMediaType(MediaTypes.PROTOBUF) @@ -223,14 +231,25 @@ public class ActivityActionTest { @Test public void support_json_response() { userSession.setGlobalPermissions(UserRole.ADMIN); - TestResponse wsResponse = tester.newRequest() + TestResponse wsResponse = ws.newRequest() .setMediaType(MediaTypes.JSON) .execute(); JsonAssert.assertJson(wsResponse.getInput()).isSimilarTo("{\"tasks\":[]}"); } - private CeActivityDto insert(String taskUuid, String componentUuid, CeActivityDto.Status status) { + private CeQueueDto insertQueue(String taskUuid, String componentUuid, CeQueueDto.Status status) { + CeQueueDto queueDto = new CeQueueDto(); + queueDto.setTaskType(CeTaskTypes.REPORT); + queueDto.setComponentUuid(componentUuid); + queueDto.setUuid(taskUuid); + queueDto.setStatus(status); + dbTester.getDbClient().ceQueueDao().insert(dbTester.getSession(), queueDto); + dbTester.commit(); + return queueDto; + } + + private CeActivityDto insertActivity(String taskUuid, String componentUuid, CeActivityDto.Status status) { CeQueueDto queueDto = new CeQueueDto(); queueDto.setTaskType(CeTaskTypes.REPORT); queueDto.setComponentUuid(componentUuid); diff --git a/sonar-db/src/main/java/org/sonar/db/ce/CeActivityDao.java b/sonar-db/src/main/java/org/sonar/db/ce/CeActivityDao.java index d4922d8c7c6..97bf1f41c4f 100644 --- a/sonar-db/src/main/java/org/sonar/db/ce/CeActivityDao.java +++ b/sonar-db/src/main/java/org/sonar/db/ce/CeActivityDao.java @@ -64,11 +64,12 @@ public class CeActivityDao implements Dao { /** * Ordered by id desc -> newest to oldest */ - public List<CeActivityDto> selectByQuery(DbSession dbSession, CeActivityQuery query, RowBounds rowBounds) { + public List<CeActivityDto> selectByQuery(DbSession dbSession, CeActivityQuery query, int offset, int pageSize) { if (query.isShortCircuitedByComponentUuids()) { return Collections.emptyList(); } - return mapper(dbSession).selectByQuery(query, rowBounds); + + return mapper(dbSession).selectByQuery(query, new RowBounds(offset, pageSize)); } public int countByQuery(DbSession dbSession, CeActivityQuery query) { diff --git a/sonar-db/src/main/java/org/sonar/db/ce/CeActivityQuery.java b/sonar-db/src/main/java/org/sonar/db/ce/CeActivityQuery.java index 8813a996436..2681554dcab 100644 --- a/sonar-db/src/main/java/org/sonar/db/ce/CeActivityQuery.java +++ b/sonar-db/src/main/java/org/sonar/db/ce/CeActivityQuery.java @@ -32,7 +32,7 @@ public class CeActivityQuery { private boolean onlyCurrents = false; private List<String> componentUuids; - private CeActivityDto.Status status; + private List<String> statuses; private String type; private Long minSubmittedAt; private Long maxExecutedAt; @@ -70,12 +70,12 @@ public class CeActivityQuery { } @CheckForNull - public CeActivityDto.Status getStatus() { - return status; + public List<String> getStatuses() { + return statuses; } - public CeActivityQuery setStatus(@Nullable CeActivityDto.Status status) { - this.status = status; + public CeActivityQuery setStatuses(@Nullable List<String> statuses) { + this.statuses = statuses; return this; } diff --git a/sonar-db/src/main/java/org/sonar/db/ce/CeQueueDao.java b/sonar-db/src/main/java/org/sonar/db/ce/CeQueueDao.java index 1b58a57ccf9..09f0b964ea6 100644 --- a/sonar-db/src/main/java/org/sonar/db/ce/CeQueueDao.java +++ b/sonar-db/src/main/java/org/sonar/db/ce/CeQueueDao.java @@ -21,12 +21,15 @@ package org.sonar.db.ce; import com.google.common.base.Optional; import java.util.List; +import org.apache.ibatis.session.RowBounds; +import org.sonar.api.utils.Paging; import org.sonar.api.utils.System2; import org.sonar.db.Dao; import org.sonar.db.DbSession; -import static org.sonar.db.ce.CeQueueDto.Status.PENDING; +import static java.util.Collections.emptyList; import static org.sonar.db.ce.CeQueueDto.Status.IN_PROGRESS; +import static org.sonar.db.ce.CeQueueDto.Status.PENDING; public class CeQueueDao implements Dao { @@ -43,6 +46,26 @@ public class CeQueueDao implements Dao { return mapper(session).selectAllInAscOrder(); } + public List<CeQueueDto> selectByQueryInDescOrder(DbSession dbSession, CeActivityQuery query, Paging paging) { + if (query.isShortCircuitedByComponentUuids() + || query.isOnlyCurrents() + || query.getMaxExecutedAt() != null) { + return emptyList(); + } + + return mapper(dbSession).selectByQueryInDescOrder(query, new RowBounds(paging.offset(), paging.pageSize())); + } + + public int countByQuery(DbSession dbSession, CeActivityQuery query) { + if (query.isShortCircuitedByComponentUuids() + || query.isOnlyCurrents() + || query.getMaxExecutedAt() != null) { + return 0; + } + + return mapper(dbSession).countByQuery(query); + } + /** * Ordered by ascending id: oldest to newest */ @@ -55,8 +78,11 @@ public class CeQueueDao implements Dao { } public CeQueueDto insert(DbSession session, CeQueueDto dto) { - dto.setCreatedAt(system2.now()); - dto.setUpdatedAt(system2.now()); + if (dto.getCreatedAt() == 0L || dto.getUpdatedAt() == 0L) { + dto.setCreatedAt(system2.now()); + dto.setUpdatedAt(system2.now()); + } + mapper(session).insert(dto); return dto; } diff --git a/sonar-db/src/main/java/org/sonar/db/ce/CeQueueMapper.java b/sonar-db/src/main/java/org/sonar/db/ce/CeQueueMapper.java index 464007b9752..fb803a73fa8 100644 --- a/sonar-db/src/main/java/org/sonar/db/ce/CeQueueMapper.java +++ b/sonar-db/src/main/java/org/sonar/db/ce/CeQueueMapper.java @@ -23,6 +23,7 @@ import java.util.List; import javax.annotation.CheckForNull; import javax.annotation.Nullable; import org.apache.ibatis.annotations.Param; +import org.apache.ibatis.session.RowBounds; public interface CeQueueMapper { @@ -30,6 +31,10 @@ public interface CeQueueMapper { List<CeQueueDto> selectAllInAscOrder(); + List<CeQueueDto> selectByQueryInDescOrder(@Param("query") CeActivityQuery query, RowBounds rowBounds); + + int countByQuery(@Param("query") CeActivityQuery query); + List<String> selectEligibleForPeek(); @CheckForNull diff --git a/sonar-db/src/main/resources/org/sonar/db/ce/CeActivityMapper.xml b/sonar-db/src/main/resources/org/sonar/db/ce/CeActivityMapper.xml index c19c97a0800..d86c8f73212 100644 --- a/sonar-db/src/main/resources/org/sonar/db/ce/CeActivityMapper.xml +++ b/sonar-db/src/main/resources/org/sonar/db/ce/CeActivityMapper.xml @@ -47,7 +47,7 @@ select <include refid="columns"/> <include refid="sqlSelectByQuery" /> - order by ca.id desc + order by ca.submitted_at desc, ca.id desc </select> <select id="countByQuery" parameterType="map" resultType="int"> @@ -67,8 +67,11 @@ #{cUuid} </foreach> </if> - <if test="query.status != null"> - and ca.status=#{query.status} + <if test="query.statuses != null and !query.statuses.isEmpty()"> + and ca.status in + <foreach collection="query.statuses" open="(" close=")" item="status" separator=","> + #{status} + </foreach> </if> <if test="query.type != null"> and ca.task_type=#{query.type} diff --git a/sonar-db/src/main/resources/org/sonar/db/ce/CeQueueMapper.xml b/sonar-db/src/main/resources/org/sonar/db/ce/CeQueueMapper.xml index f43a9e9b30c..b00fadad36a 100644 --- a/sonar-db/src/main/resources/org/sonar/db/ce/CeQueueMapper.xml +++ b/sonar-db/src/main/resources/org/sonar/db/ce/CeQueueMapper.xml @@ -18,6 +18,10 @@ order by cq.created_at asc, cq.id asc </sql> + <sql id="orderByDescDateAndId"> + order by cq.created_at desc, cq.id asc + </sql> + <select id="selectByUuid" parameterType="String" resultType="org.sonar.db.ce.CeQueueDto"> select <include refid="columns"/> @@ -48,6 +52,42 @@ <include refid="orderByDateAndId"/> </select> + <select id="selectByQueryInDescOrder" resultType="org.sonar.db.ce.CeQueueDto"> + select + <include refid="columns"/> + <include refid="sqlSelectByQuery"/> + <include refid="orderByDescDateAndId"/> + </select> + + <select id="countByQuery" resultType="int"> + select count(id) + <include refid="sqlSelectByQuery"/> + </select> + + <sql id="sqlSelectByQuery"> + from ce_queue cq + <where> + <if test="query.componentUuids != null and query.componentUuids.size()>0"> + and cq.component_uuid in + <foreach collection="query.componentUuids" open="(" close=")" item="cUuid" separator=","> + #{cUuid} + </foreach> + </if> + <if test="query.statuses != null"> + and cq.status in + <foreach collection="query.statuses" open="(" close=")" item="status" separator=","> + #{status} + </foreach> + </if> + <if test="query.type != null"> + and cq.task_type=#{query.type} + </if> + <if test="query.minSubmittedAt != null"> + and cq.created_at >= #{query.minSubmittedAt} + </if> + </where> + </sql> + <select id="selectEligibleForPeek" resultType="String"> select cq.uuid from ce_queue cq diff --git a/sonar-db/src/test/java/org/sonar/db/ce/CeActivityDaoTest.java b/sonar-db/src/test/java/org/sonar/db/ce/CeActivityDaoTest.java index c1a15756f9b..cf726322310 100644 --- a/sonar-db/src/test/java/org/sonar/db/ce/CeActivityDaoTest.java +++ b/sonar-db/src/test/java/org/sonar/db/ce/CeActivityDaoTest.java @@ -22,7 +22,6 @@ package org.sonar.db.ce; import com.google.common.base.Optional; import java.util.Collections; import java.util.List; -import org.apache.ibatis.session.RowBounds; import org.junit.Rule; import org.junit.Test; import org.junit.experimental.categories.Category; @@ -30,6 +29,7 @@ import org.sonar.api.utils.internal.TestSystem2; import org.sonar.db.DbTester; import org.sonar.test.DbTests; +import static java.util.Collections.singletonList; import static org.assertj.core.api.Assertions.assertThat; import static org.sonar.db.ce.CeTaskTypes.REPORT; @@ -96,31 +96,31 @@ public class CeActivityDaoTest { insert("TASK_4", "views", null, CeActivityDto.Status.SUCCESS); // no filters - CeActivityQuery query = new CeActivityQuery(); - List<CeActivityDto> dtos = underTest.selectByQuery(db.getSession(), query, new RowBounds(0, 10)); + CeActivityQuery query = new CeActivityQuery().setStatuses(Collections.<String>emptyList()); + List<CeActivityDto> dtos = underTest.selectByQuery(db.getSession(), query, 0, 10); assertThat(dtos).extracting("uuid").containsExactly("TASK_4", "TASK_3", "TASK_2", "TASK_1"); // select by component uuid query = new CeActivityQuery().setComponentUuid("PROJECT_1"); - dtos = underTest.selectByQuery(db.getSession(), query, new RowBounds(0, 10)); + dtos = underTest.selectByQuery(db.getSession(), query, 0, 100); assertThat(dtos).extracting("uuid").containsExactly("TASK_2", "TASK_1"); // select by status - query = new CeActivityQuery().setStatus(CeActivityDto.Status.SUCCESS); - dtos = underTest.selectByQuery(db.getSession(), query, new RowBounds(0, 10)); + query = new CeActivityQuery().setStatuses(singletonList(CeActivityDto.Status.SUCCESS.name())); + dtos = underTest.selectByQuery(db.getSession(), query, 0, 100); assertThat(dtos).extracting("uuid").containsExactly("TASK_4", "TASK_3", "TASK_1"); // select by type query = new CeActivityQuery().setType(REPORT); - dtos = underTest.selectByQuery(db.getSession(), query, new RowBounds(0, 10)); + dtos = underTest.selectByQuery(db.getSession(), query, 0, 100); assertThat(dtos).extracting("uuid").containsExactly("TASK_3", "TASK_2", "TASK_1"); query = new CeActivityQuery().setType("views"); - dtos = underTest.selectByQuery(db.getSession(), query, new RowBounds(0, 10)); + dtos = underTest.selectByQuery(db.getSession(), query, 0, 100); assertThat(dtos).extracting("uuid").containsExactly("TASK_4"); // select by multiple conditions query = new CeActivityQuery().setType(REPORT).setOnlyCurrents(true).setComponentUuid("PROJECT_1"); - dtos = underTest.selectByQuery(db.getSession(), query, new RowBounds(0, 10)); + dtos = underTest.selectByQuery(db.getSession(), query, 0, 100); assertThat(dtos).extracting("uuid").containsExactly("TASK_2"); } @@ -140,7 +140,7 @@ public class CeActivityDaoTest { assertThat(underTest.countByQuery(db.getSession(), query)).isEqualTo(2); // select by status - query = new CeActivityQuery().setStatus(CeActivityDto.Status.SUCCESS); + query = new CeActivityQuery().setStatuses(singletonList(CeActivityDto.Status.SUCCESS.name())); assertThat(underTest.countByQuery(db.getSession(), query)).isEqualTo(3); // select by type @@ -160,7 +160,7 @@ public class CeActivityDaoTest { CeActivityQuery query = new CeActivityQuery(); query.setComponentUuids(Collections.<String>emptyList()); - assertThat(underTest.selectByQuery(db.getSession(), query, new RowBounds(0, 10))).isEmpty(); + assertThat(underTest.selectByQuery(db.getSession(), query, 0, 0)).isEmpty(); } @Test @@ -179,19 +179,19 @@ public class CeActivityDaoTest { // search by min submitted date CeActivityQuery query = new CeActivityQuery().setMinSubmittedAt(1_455_000_000_000L); - assertThat(underTest.selectByQuery(db.getSession(), query, new RowBounds(0, 10))).extracting("uuid").containsOnly("UUID2"); + assertThat(underTest.selectByQuery(db.getSession(), query, 0, 5)).extracting("uuid").containsOnly("UUID2"); assertThat(underTest.countByQuery(db.getSession(), query)).isEqualTo(1); // search by max executed date query = new CeActivityQuery().setMaxExecutedAt(1_475_000_000_000L); - assertThat(underTest.selectByQuery(db.getSession(), query, new RowBounds(0, 10))).extracting("uuid").containsOnly("UUID1"); + assertThat(underTest.selectByQuery(db.getSession(), query, 0, 5)).extracting("uuid").containsOnly("UUID1"); assertThat(underTest.countByQuery(db.getSession(), query)).isEqualTo(1); // search by both dates query = new CeActivityQuery() .setMinSubmittedAt(1_400_000_000_000L) .setMaxExecutedAt(1_475_000_000_000L); - assertThat(underTest.selectByQuery(db.getSession(), query, new RowBounds(0, 10))).extracting("uuid").containsOnly("UUID1"); + assertThat(underTest.selectByQuery(db.getSession(), query, 0, 5)).extracting("uuid").containsOnly("UUID1"); assertThat(underTest.countByQuery(db.getSession(), query)).isEqualTo(1); } diff --git a/sonar-db/src/test/java/org/sonar/db/ce/CeQueueDaoTest.java b/sonar-db/src/test/java/org/sonar/db/ce/CeQueueDaoTest.java index 247f735430e..943fbc14e52 100644 --- a/sonar-db/src/test/java/org/sonar/db/ce/CeQueueDaoTest.java +++ b/sonar-db/src/test/java/org/sonar/db/ce/CeQueueDaoTest.java @@ -20,15 +20,20 @@ package org.sonar.db.ce; import com.google.common.base.Optional; +import java.util.List; import org.junit.Rule; import org.junit.Test; import org.junit.experimental.categories.Category; +import org.sonar.api.utils.Paging; import org.sonar.api.utils.System2; import org.sonar.api.utils.internal.TestSystem2; import org.sonar.db.DbTester; import org.sonar.test.DbTests; +import static com.google.common.collect.Lists.newArrayList; +import static java.util.Collections.singletonList; import static org.assertj.core.api.Assertions.assertThat; +import static org.sonar.db.ce.CeQueueTesting.newCeQueueDto; @Category(DbTests.class) public class CeQueueDaoTest { @@ -169,6 +174,61 @@ public class CeQueueDaoTest { assertThat(peek.get().getUuid()).isEqualTo("TASK_2"); } + @Test + public void select_by_query() { + // task status not in query + insert(newCeQueueDto("TASK_1") + .setComponentUuid("PROJECT_1") + .setStatus(CeQueueDto.Status.IN_PROGRESS) + .setTaskType(CeTaskTypes.REPORT) + .setCreatedAt(100_000L)); + + // too early + insert(newCeQueueDto("TASK_3") + .setComponentUuid("PROJECT_1") + .setStatus(CeQueueDto.Status.PENDING) + .setTaskType(CeTaskTypes.REPORT) + .setCreatedAt(90_000L)); + + // task type not in query + insert(newCeQueueDto("TASK_4") + .setComponentUuid("PROJECT_2") + .setStatus(CeQueueDto.Status.PENDING) + .setTaskType("ANOTHER_TYPE") + .setCreatedAt(100_000L)); + + // correct + insert(newCeQueueDto("TASK_2") + .setComponentUuid("PROJECT_1") + .setStatus(CeQueueDto.Status.PENDING) + .setTaskType(CeTaskTypes.REPORT) + .setCreatedAt(100_000L)); + + // correct submitted later + insert(newCeQueueDto("TASK_5") + .setComponentUuid("PROJECT_1") + .setStatus(CeQueueDto.Status.PENDING) + .setTaskType(CeTaskTypes.REPORT) + .setCreatedAt(120_000L)); + + CeActivityQuery query = new CeActivityQuery() + .setComponentUuids(newArrayList("PROJECT_1", "PROJECT_2")) + .setStatuses(singletonList(CeQueueDto.Status.PENDING.name())) + .setType(CeTaskTypes.REPORT) + .setMinSubmittedAt(100_000L); + + List<CeQueueDto> result = underTest.selectByQueryInDescOrder(db.getSession(), query, Paging.forPageIndex(1).withPageSize(1_000).andTotal(1_000)); + int total = underTest.countByQuery(db.getSession(), query); + + assertThat(result).extracting("uuid").containsExactly("TASK_5", "TASK_2"); + assertThat(total).isEqualTo(2); + } + + private void insert(CeQueueDto dto) { + underTest.insert(db.getSession(), dto); + db.commit(); + } + private void insert(String uuid, String componentUuid, CeQueueDto.Status status) { CeQueueDto dto = new CeQueueDto(); dto.setUuid(uuid); @@ -177,6 +237,6 @@ public class CeQueueDaoTest { dto.setStatus(status); dto.setSubmitterLogin("henri"); underTest.insert(db.getSession(), dto); - db.getSession().commit(); + db.commit(); } } diff --git a/sonar-db/src/test/java/org/sonar/db/ce/CeQueueTesting.java b/sonar-db/src/test/java/org/sonar/db/ce/CeQueueTesting.java index 7e8974421ba..0f0a7564018 100644 --- a/sonar-db/src/test/java/org/sonar/db/ce/CeQueueTesting.java +++ b/sonar-db/src/test/java/org/sonar/db/ce/CeQueueTesting.java @@ -38,4 +38,6 @@ public class CeQueueTesting { .setUpdatedAt(nextLong()) .setStartedAt(nextLong()); } + + } diff --git a/sonar-ws/src/main/java/org/sonarqube/ws/client/HttpWsClient.java b/sonar-ws/src/main/java/org/sonarqube/ws/client/HttpWsClient.java index 664960aec8d..abec779c8c5 100644 --- a/sonar-ws/src/main/java/org/sonarqube/ws/client/HttpWsClient.java +++ b/sonar-ws/src/main/java/org/sonarqube/ws/client/HttpWsClient.java @@ -19,6 +19,7 @@ */ package org.sonarqube.ws.client; +import org.sonarqube.ws.client.ce.CeService; import org.sonarqube.ws.client.component.ComponentsService; import org.sonarqube.ws.client.issue.IssuesService; import org.sonarqube.ws.client.measure.MeasuresService; @@ -35,6 +36,7 @@ import org.sonarqube.ws.client.usertoken.UserTokensService; */ public class HttpWsClient implements WsClient { + private final WsConnector wsConnector; private final PermissionsService permissionsService; private final ComponentsService componentsService; private final QualityProfilesService qualityProfilesService; @@ -43,7 +45,7 @@ public class HttpWsClient implements WsClient { private final QualityGatesService qualityGatesService; private final MeasuresService measuresService; private final SystemService systemService; - private final WsConnector wsConnector; + private final CeService ceService; public HttpWsClient(WsConnector wsConnector) { this.wsConnector = wsConnector; @@ -55,6 +57,7 @@ public class HttpWsClient implements WsClient { this.qualityGatesService = new QualityGatesService(wsConnector); this.measuresService = new MeasuresService(wsConnector); this.systemService = new SystemService(wsConnector); + this.ceService = new CeService(wsConnector); } @Override @@ -101,4 +104,9 @@ public class HttpWsClient implements WsClient { public SystemService system() { return systemService; } + + @Override + public CeService ce() { + return ceService; + } } diff --git a/sonar-ws/src/main/java/org/sonarqube/ws/client/WsClient.java b/sonar-ws/src/main/java/org/sonarqube/ws/client/WsClient.java index db7ab7ba245..7f865292fc7 100644 --- a/sonar-ws/src/main/java/org/sonarqube/ws/client/WsClient.java +++ b/sonar-ws/src/main/java/org/sonarqube/ws/client/WsClient.java @@ -19,6 +19,7 @@ */ package org.sonarqube.ws.client; +import org.sonarqube.ws.client.ce.CeService; import org.sonarqube.ws.client.component.ComponentsService; import org.sonarqube.ws.client.issue.IssuesService; import org.sonarqube.ws.client.measure.MeasuresService; @@ -48,5 +49,7 @@ public interface WsClient { SystemService system(); + CeService ce(); + WsConnector wsConnector(); } diff --git a/sonar-ws/src/main/java/org/sonarqube/ws/client/ce/ActivityWsRequest.java b/sonar-ws/src/main/java/org/sonarqube/ws/client/ce/ActivityWsRequest.java new file mode 100644 index 00000000000..a0e53ace029 --- /dev/null +++ b/sonar-ws/src/main/java/org/sonarqube/ws/client/ce/ActivityWsRequest.java @@ -0,0 +1,127 @@ +/* + * SonarQube + * Copyright (C) 2009-2016 SonarSource SA + * mailto:contact AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +package org.sonarqube.ws.client.ce; + +import java.util.List; +import javax.annotation.CheckForNull; +import javax.annotation.Nullable; + +public class ActivityWsRequest { + private String componentId; + private String componentQuery; + private List<String> status; + private String type; + private Boolean onlyCurrents; + private String minSubmittedAt; + private String maxExecutedAt; + private Integer page; + private Integer pageSize; + + @CheckForNull + public String getComponentId() { + return componentId; + } + + public ActivityWsRequest setComponentId(@Nullable String componentId) { + this.componentId = componentId; + return this; + } + + @CheckForNull + public String getComponentQuery() { + return componentQuery; + } + + public ActivityWsRequest setComponentQuery(@Nullable String componentQuery) { + this.componentQuery = componentQuery; + return this; + } + + @CheckForNull + public List<String> getStatus() { + return status; + } + + public ActivityWsRequest setStatus(@Nullable List<String> status) { + this.status = status; + return this; + } + + @CheckForNull + public String getType() { + return type; + } + + public ActivityWsRequest setType(@Nullable String type) { + this.type = type; + return this; + } + + @CheckForNull + public Boolean getOnlyCurrents() { + return onlyCurrents; + } + + public ActivityWsRequest setOnlyCurrents(@Nullable Boolean onlyCurrents) { + this.onlyCurrents = onlyCurrents; + return this; + } + + @CheckForNull + public String getMinSubmittedAt() { + return minSubmittedAt; + } + + public ActivityWsRequest setMinSubmittedAt(@Nullable String minSubmittedAt) { + this.minSubmittedAt = minSubmittedAt; + return this; + } + + @CheckForNull + public String getMaxExecutedAt() { + return maxExecutedAt; + } + + public ActivityWsRequest setMaxExecutedAt(@Nullable String maxExecutedAt) { + this.maxExecutedAt = maxExecutedAt; + return this; + } + + @CheckForNull + public Integer getPage() { + return page; + } + + public ActivityWsRequest setPage(@Nullable Integer page) { + this.page = page; + return this; + } + + @CheckForNull + public Integer getPageSize() { + return pageSize; + } + + public ActivityWsRequest setPageSize(@Nullable Integer pageSize) { + this.pageSize = pageSize; + return this; + } +} diff --git a/sonar-ws/src/main/java/org/sonarqube/ws/client/ce/CeService.java b/sonar-ws/src/main/java/org/sonarqube/ws/client/ce/CeService.java new file mode 100644 index 00000000000..b11cfdfb60e --- /dev/null +++ b/sonar-ws/src/main/java/org/sonarqube/ws/client/ce/CeService.java @@ -0,0 +1,57 @@ +/* + * SonarQube + * Copyright (C) 2009-2016 SonarSource SA + * mailto:contact AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +package org.sonarqube.ws.client.ce; + +import org.sonarqube.ws.WsCe.ActivityResponse; +import org.sonarqube.ws.client.BaseService; +import org.sonarqube.ws.client.GetRequest; +import org.sonarqube.ws.client.WsConnector; + +import static org.sonarqube.ws.client.ce.CeWsParameters.PARAM_COMPONENT_ID; +import static org.sonarqube.ws.client.ce.CeWsParameters.PARAM_COMPONENT_QUERY; +import static org.sonarqube.ws.client.ce.CeWsParameters.PARAM_MAX_EXECUTED_AT; +import static org.sonarqube.ws.client.ce.CeWsParameters.PARAM_MIN_SUBMITTED_AT; +import static org.sonarqube.ws.client.ce.CeWsParameters.PARAM_ONLY_CURRENTS; +import static org.sonarqube.ws.client.ce.CeWsParameters.PARAM_STATUS; +import static org.sonarqube.ws.client.ce.CeWsParameters.PARAM_TYPE; + +public class CeService extends BaseService { + + public CeService(WsConnector wsConnector) { + super(wsConnector, "api/ce"); + } + + public ActivityResponse activity(ActivityWsRequest request) { + return call( + new GetRequest(path("activity")) + .setParam(PARAM_COMPONENT_ID, request.getComponentId()) + .setParam(PARAM_COMPONENT_QUERY, request.getComponentQuery()) + .setParam(PARAM_STATUS, inlineMultipleParamValue(request.getStatus())) + .setParam(PARAM_TYPE, request.getType()) + .setParam(PARAM_MAX_EXECUTED_AT, request.getMaxExecutedAt()) + .setParam(PARAM_MIN_SUBMITTED_AT, request.getMinSubmittedAt()) + .setParam(PARAM_ONLY_CURRENTS, request.getOnlyCurrents()) + .setParam("p", request.getPage()) + .setParam("ps", request.getPageSize()), + ActivityResponse.parser()); + } + +} diff --git a/sonar-ws/src/main/java/org/sonarqube/ws/client/ce/CeWsParameters.java b/sonar-ws/src/main/java/org/sonarqube/ws/client/ce/CeWsParameters.java new file mode 100644 index 00000000000..3b4fc97e4f1 --- /dev/null +++ b/sonar-ws/src/main/java/org/sonarqube/ws/client/ce/CeWsParameters.java @@ -0,0 +1,35 @@ +/* + * SonarQube + * Copyright (C) 2009-2016 SonarSource SA + * mailto:contact AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +package org.sonarqube.ws.client.ce; + +public class CeWsParameters { + public static final String PARAM_COMPONENT_ID = "componentId"; + public static final String PARAM_COMPONENT_QUERY = "componentQuery"; + public static final String PARAM_TYPE = "type"; + public static final String PARAM_STATUS = "status"; + public static final String PARAM_ONLY_CURRENTS = "onlyCurrents"; + public static final String PARAM_MIN_SUBMITTED_AT = "minSubmittedAt"; + public static final String PARAM_MAX_EXECUTED_AT = "maxExecutedAt"; + + private CeWsParameters() { + // prevent instantiation + } +} diff --git a/sonar-ws/src/main/java/org/sonarqube/ws/client/ce/package-info.java b/sonar-ws/src/main/java/org/sonarqube/ws/client/ce/package-info.java new file mode 100644 index 00000000000..46846e51219 --- /dev/null +++ b/sonar-ws/src/main/java/org/sonarqube/ws/client/ce/package-info.java @@ -0,0 +1,25 @@ +/* + * SonarQube + * Copyright (C) 2009-2016 SonarSource SA + * mailto:contact AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +@ParametersAreNonnullByDefault +package org.sonarqube.ws.client.ce; + +import javax.annotation.ParametersAreNonnullByDefault; + diff --git a/sonar-ws/src/test/java/org/sonarqube/ws/client/ce/CeServiceTest.java b/sonar-ws/src/test/java/org/sonarqube/ws/client/ce/CeServiceTest.java new file mode 100644 index 00000000000..d7094f75495 --- /dev/null +++ b/sonar-ws/src/test/java/org/sonarqube/ws/client/ce/CeServiceTest.java @@ -0,0 +1,90 @@ +/* + * SonarQube + * Copyright (C) 2009-2016 SonarSource SA + * mailto:contact AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +package org.sonarqube.ws.client.ce; + +import com.google.common.collect.ImmutableList; +import org.junit.Rule; +import org.junit.Test; +import org.sonarqube.ws.WsCe.ActivityResponse; +import org.sonarqube.ws.client.GetRequest; +import org.sonarqube.ws.client.ServiceTester; +import org.sonarqube.ws.client.WsConnector; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.mock; +import static org.sonarqube.ws.client.ce.CeWsParameters.PARAM_COMPONENT_ID; +import static org.sonarqube.ws.client.ce.CeWsParameters.PARAM_COMPONENT_QUERY; +import static org.sonarqube.ws.client.ce.CeWsParameters.PARAM_MAX_EXECUTED_AT; +import static org.sonarqube.ws.client.ce.CeWsParameters.PARAM_MIN_SUBMITTED_AT; +import static org.sonarqube.ws.client.ce.CeWsParameters.PARAM_ONLY_CURRENTS; +import static org.sonarqube.ws.client.ce.CeWsParameters.PARAM_STATUS; +import static org.sonarqube.ws.client.ce.CeWsParameters.PARAM_TYPE; + +public class CeServiceTest { + private static final String VALUE_COMPONENT_ID = "component-uuid"; + private static final String VALUE_COMPONENT_QUERY = "component-query"; + private static final String VALUE_TASK_STATUS_1 = "task-status"; + private static final String VALUE_TASK_STATUS_2 = "task-status-2"; + private static final String VALUE_TASK_TYPE = "task-type"; + private static final int VALUE_PAGE = 1; + private static final int VALUE_PAGE_SIZE = 10; + private static final String VALUE_MAX_EXECUTED_AT = "2015-09-17T23:34:59+0200"; + private static final String VALUE_MIN_SUBMITTED_AT = "2015-09-17T23:34:59+0200"; + private static final boolean VALUE_ONLY_CURRENTS = true; + + @Rule + public ServiceTester<CeService> serviceTester = new ServiceTester<>(new CeService(mock(WsConnector.class))); + + CeService underTest = serviceTester.getInstanceUnderTest(); + + @Test + public void search() { + ActivityWsRequest request = new ActivityWsRequest() + .setComponentId(VALUE_COMPONENT_ID) + .setComponentQuery(VALUE_COMPONENT_QUERY) + .setStatus(ImmutableList.of(VALUE_TASK_STATUS_1, VALUE_TASK_STATUS_2)) + .setType(VALUE_TASK_TYPE) + .setPage(VALUE_PAGE) + .setPageSize(VALUE_PAGE_SIZE) + .setMaxExecutedAt(VALUE_MAX_EXECUTED_AT) + .setMinSubmittedAt(VALUE_MIN_SUBMITTED_AT) + .setOnlyCurrents(VALUE_ONLY_CURRENTS) + .setPage(1) + .setPageSize(1); + + underTest.activity(request); + GetRequest result = serviceTester.getGetRequest(); + + assertThat(serviceTester.getGetParser()).isSameAs(ActivityResponse.parser()); + serviceTester.assertThat(result) + .hasPath("activity") + .hasParam(PARAM_COMPONENT_ID, VALUE_COMPONENT_ID) + .hasParam(PARAM_COMPONENT_QUERY, VALUE_COMPONENT_QUERY) + .hasParam(PARAM_STATUS, VALUE_TASK_STATUS_1 + "," + VALUE_TASK_STATUS_2) + .hasParam(PARAM_TYPE, VALUE_TASK_TYPE) + .hasParam(PARAM_MAX_EXECUTED_AT, VALUE_MAX_EXECUTED_AT) + .hasParam(PARAM_MIN_SUBMITTED_AT, VALUE_MIN_SUBMITTED_AT) + .hasParam(PARAM_ONLY_CURRENTS, VALUE_ONLY_CURRENTS) + .hasParam("p", 1) + .hasParam("ps", 1) + .andNoOtherParam(); + } +} diff --git a/sonar-ws/src/test/java/org/sonarqube/ws/client/issue/SearchWsRequestTest.java b/sonar-ws/src/test/java/org/sonarqube/ws/client/issue/ActivityWsRequestTest.java index 9fc4bac7ccd..c8115e322d0 100644 --- a/sonar-ws/src/test/java/org/sonarqube/ws/client/issue/SearchWsRequestTest.java +++ b/sonar-ws/src/test/java/org/sonarqube/ws/client/issue/ActivityWsRequestTest.java @@ -24,7 +24,7 @@ import org.junit.Test; import static org.assertj.core.api.Assertions.assertThat; -public class SearchWsRequestTest { +public class ActivityWsRequestTest { private static final ImmutableList<String> LIST_OF_STRINGS = ImmutableList.of("A", "B"); private static final String SOME_STRING = "some string"; public static final int SOME_INT = 894352; |