diff options
6 files changed, 98 insertions, 27 deletions
diff --git a/server/sonar-server/src/main/java/org/sonar/server/component/ws/ComponentDtoToWsComponent.java b/server/sonar-server/src/main/java/org/sonar/server/component/ws/ComponentDtoToWsComponent.java index 63951b30c39..cc5e7aaf032 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/component/ws/ComponentDtoToWsComponent.java +++ b/server/sonar-server/src/main/java/org/sonar/server/component/ws/ComponentDtoToWsComponent.java @@ -20,12 +20,15 @@ package org.sonar.server.component.ws; import java.util.Objects; +import java.util.Optional; import org.sonar.db.component.ComponentDto; +import org.sonar.db.component.SnapshotDto; import org.sonar.db.organization.OrganizationDto; import org.sonarqube.ws.WsComponents; import static com.google.common.base.Preconditions.checkArgument; import static com.google.common.base.Strings.emptyToNull; +import static org.sonar.api.utils.DateUtils.formatDateTime; import static org.sonar.core.util.Protobuf.setNullable; class ComponentDtoToWsComponent { @@ -33,15 +36,15 @@ class ComponentDtoToWsComponent { // prevent instantiation } - static WsComponents.Component.Builder componentDtoToWsComponent(ComponentDto dto, OrganizationDto organizationDto) { + static WsComponents.Component.Builder componentDtoToWsComponent(ComponentDto dto, OrganizationDto organizationDto, Optional<SnapshotDto> lastAnalysis) { checkArgument( Objects.equals(dto.getOrganizationUuid(), organizationDto.getUuid()), "OrganizationUuid (%s) of ComponentDto to convert to Ws Component is not the same as the one (%s) of the specified OrganizationDto", dto.getOrganizationUuid(), organizationDto.getUuid()); - return componentDtoToWsComponent(dto, organizationDto.getKey()); + return componentDtoToWsComponent(dto, organizationDto.getKey(), lastAnalysis); } - private static WsComponents.Component.Builder componentDtoToWsComponent(ComponentDto dto, String organizationDtoKey) { + private static WsComponents.Component.Builder componentDtoToWsComponent(ComponentDto dto, String organizationDtoKey, Optional<SnapshotDto> lastAnalysis) { WsComponents.Component.Builder wsComponent = WsComponents.Component.newBuilder() .setOrganization(organizationDtoKey) .setId(dto.uuid()) @@ -51,6 +54,7 @@ class ComponentDtoToWsComponent { setNullable(emptyToNull(dto.path()), wsComponent::setPath); setNullable(emptyToNull(dto.description()), wsComponent::setDescription); setNullable(emptyToNull(dto.language()), wsComponent::setLanguage); + lastAnalysis.ifPresent(analysis -> wsComponent.setAnalysisDate(formatDateTime(analysis.getCreatedAt()))); return wsComponent; } } diff --git a/server/sonar-server/src/main/java/org/sonar/server/component/ws/ShowAction.java b/server/sonar-server/src/main/java/org/sonar/server/component/ws/ShowAction.java index b65ae45a1e0..04f1aafca1c 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/component/ws/ShowAction.java +++ b/server/sonar-server/src/main/java/org/sonar/server/component/ws/ShowAction.java @@ -20,6 +20,9 @@ package org.sonar.server.component.ws; import java.util.List; +import java.util.Optional; +import java.util.stream.IntStream; +import org.sonar.api.server.ws.Change; import org.sonar.api.server.ws.Request; import org.sonar.api.server.ws.Response; import org.sonar.api.server.ws.WebService; @@ -27,6 +30,7 @@ import org.sonar.api.web.UserRole; import org.sonar.db.DbClient; import org.sonar.db.DbSession; import org.sonar.db.component.ComponentDto; +import org.sonar.db.component.SnapshotDto; import org.sonar.db.organization.OrganizationDto; import org.sonar.server.component.ComponentFinder; import org.sonar.server.component.ComponentFinder.ParamNames; @@ -54,26 +58,6 @@ public class ShowAction implements ComponentsWsAction { this.componentFinder = componentFinder; } - private static ShowWsResponse buildResponse(ComponentDto component, OrganizationDto organizationDto, List<ComponentDto> orderedAncestors) { - ShowWsResponse.Builder response = ShowWsResponse.newBuilder(); - response.setComponent(componentDtoToWsComponent(component, organizationDto)); - - // ancestors are ordered from root to leaf, whereas it's the opposite - // in WS response - for (int i = orderedAncestors.size() - 1; i >= 0; i--) { - ComponentDto ancestor = orderedAncestors.get(i); - response.addAncestors(componentDtoToWsComponent(ancestor, organizationDto)); - } - - return response.build(); - } - - private static ShowWsRequest toShowWsRequest(Request request) { - return new ShowWsRequest() - .setId(request.param(PARAM_COMPONENT_ID)) - .setKey(request.param(PARAM_COMPONENT)); - } - @Override public void define(WebService.NewController context) { WebService.NewAction action = context.createAction(ACTION_SHOW) @@ -84,6 +68,7 @@ public class ShowAction implements ComponentsWsAction { PARAM_COMPONENT_ID, PARAM_COMPONENT)) .setResponseExample(getClass().getResource("show-example.json")) .setSince("5.4") + .setChangelog(new Change("6.4", "Analysis date has been added to the response")) .setHandler(this); action.createParam(PARAM_COMPONENT_ID) @@ -109,9 +94,10 @@ public class ShowAction implements ComponentsWsAction { DbSession dbSession = dbClient.openSession(false); try { ComponentDto component = getComponentByUuidOrKey(dbSession, request); + Optional<SnapshotDto> lastAnalysis = dbClient.snapshotDao().selectLastAnalysisByComponentUuid(dbSession, component.projectUuid()); List<ComponentDto> ancestors = dbClient.componentDao().selectAncestors(dbSession, component); OrganizationDto organizationDto = componentFinder.getOrganization(dbSession, component); - return buildResponse(component, organizationDto, ancestors); + return buildResponse(component, organizationDto, ancestors, lastAnalysis); } finally { dbClient.closeSession(dbSession); } @@ -122,4 +108,21 @@ public class ShowAction implements ComponentsWsAction { userSession.checkComponentPermission(UserRole.USER, component); return component; } + + private static ShowWsResponse buildResponse(ComponentDto component, OrganizationDto organizationDto, List<ComponentDto> orderedAncestors, Optional<SnapshotDto> lastAnalysis) { + ShowWsResponse.Builder response = ShowWsResponse.newBuilder(); + response.setComponent(componentDtoToWsComponent(component, organizationDto, lastAnalysis)); + + // ancestors are ordered from root to leaf, whereas it's the opposite in WS response + int size = orderedAncestors.size() - 1; + IntStream.rangeClosed(0, size).forEach( + index -> response.addAncestors(componentDtoToWsComponent(orderedAncestors.get(size - index), organizationDto, lastAnalysis))); + return response.build(); + } + + private static ShowWsRequest toShowWsRequest(Request request) { + return new ShowWsRequest() + .setId(request.param(PARAM_COMPONENT_ID)) + .setKey(request.param(PARAM_COMPONENT)); + } } diff --git a/server/sonar-server/src/main/java/org/sonar/server/component/ws/TreeAction.java b/server/sonar-server/src/main/java/org/sonar/server/component/ws/TreeAction.java index b01861db1c7..c5e8be4a337 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/component/ws/TreeAction.java +++ b/server/sonar-server/src/main/java/org/sonar/server/component/ws/TreeAction.java @@ -28,6 +28,7 @@ import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.Objects; +import java.util.Optional; import java.util.Set; import javax.annotation.CheckForNull; import org.sonar.api.i18n.I18n; @@ -63,8 +64,8 @@ import static org.sonar.db.component.ComponentTreeQuery.Strategy.LEAVES; import static org.sonar.server.component.ComponentFinder.ParamNames.COMPONENT_ID_AND_COMPONENT; import static org.sonar.server.component.ws.ComponentDtoToWsComponent.componentDtoToWsComponent; import static org.sonar.server.ws.KeyExamples.KEY_PROJECT_EXAMPLE_001; -import static org.sonar.server.ws.WsParameterBuilder.QualifierParameterContext.newQualifierParameterContext; import static org.sonar.server.ws.WsParameterBuilder.createQualifiersParameter; +import static org.sonar.server.ws.WsParameterBuilder.QualifierParameterContext.newQualifierParameterContext; import static org.sonar.server.ws.WsUtils.checkRequest; import static org.sonar.server.ws.WsUtils.writeProtobuf; import static org.sonarqube.ws.client.component.ComponentsWsParameters.ACTION_TREE; @@ -213,7 +214,7 @@ public class TreeAction implements ComponentsWsAction { private static WsComponents.Component.Builder toWsComponent(ComponentDto component, OrganizationDto organizationDto, Map<String, ComponentDto> referenceComponentsByUuid) { - WsComponents.Component.Builder wsComponent = componentDtoToWsComponent(component, organizationDto); + WsComponents.Component.Builder wsComponent = componentDtoToWsComponent(component, organizationDto, Optional.empty()); ComponentDto referenceComponent = referenceComponentsByUuid.get(component.getCopyResourceUuid()); if (referenceComponent != null) { diff --git a/server/sonar-server/src/test/java/org/sonar/server/component/ws/ComponentDtoToWsComponentTest.java b/server/sonar-server/src/test/java/org/sonar/server/component/ws/ComponentDtoToWsComponentTest.java index d2a2bfc0ba5..d79b53be798 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/component/ws/ComponentDtoToWsComponentTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/component/ws/ComponentDtoToWsComponentTest.java @@ -19,6 +19,7 @@ */ package org.sonar.server.component.ws; +import java.util.Optional; import org.junit.Rule; import org.junit.Test; import org.junit.rules.ExpectedException; @@ -43,7 +44,7 @@ public class ComponentDtoToWsComponentTest { expectedException.expectMessage("OrganizationUuid (" + organizationDto1.getUuid() + ") of ComponentDto to convert " + "to Ws Component is not the same as the one (" + organizationDto2.getUuid() + ") of the specified OrganizationDto"); - componentDtoToWsComponent(componentDto, organizationDto2); + componentDtoToWsComponent(componentDto, organizationDto2, Optional.empty()); } } diff --git a/server/sonar-server/src/test/java/org/sonar/server/component/ws/ShowActionTest.java b/server/sonar-server/src/test/java/org/sonar/server/component/ws/ShowActionTest.java index 6dc3dbb8ab1..e384589f0f2 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/component/ws/ShowActionTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/component/ws/ShowActionTest.java @@ -22,6 +22,7 @@ package org.sonar.server.component.ws; import com.google.common.base.Throwables; import java.io.IOException; import java.io.InputStream; +import java.util.Date; import javax.annotation.Nullable; import org.junit.Rule; import org.junit.Test; @@ -40,12 +41,16 @@ import org.sonar.server.tester.UserSessionRule; import org.sonar.server.ws.TestRequest; import org.sonar.server.ws.WsActionTester; import org.sonarqube.ws.MediaTypes; +import org.sonarqube.ws.WsComponents; import org.sonarqube.ws.WsComponents.ShowWsResponse; import static org.assertj.core.api.Assertions.assertThat; +import static org.sonar.api.utils.DateUtils.formatDateTime; import static org.sonar.db.component.ComponentTesting.newDirectory; import static org.sonar.db.component.ComponentTesting.newFileDto; +import static org.sonar.db.component.ComponentTesting.newModuleDto; import static org.sonar.db.component.ComponentTesting.newProjectDto; +import static org.sonar.db.component.SnapshotTesting.newAnalysis; import static org.sonar.test.JsonAssert.assertJson; import static org.sonarqube.ws.client.component.ComponentsWsParameters.PARAM_COMPONENT; import static org.sonarqube.ws.client.component.ComponentsWsParameters.PARAM_COMPONENT_ID; @@ -93,6 +98,62 @@ public class ShowActionTest { ShowWsResponse response = newRequest("project-uuid", null); assertThat(response.getComponent().getId()).isEqualTo("project-uuid"); + assertThat(response.getComponent().hasAnalysisDate()).isFalse(); + } + + @Test + public void show_with_ancestors_when_not_project() throws Exception { + ComponentDto project = componentDb.insertProject(); + ComponentDto module = componentDb.insertComponent(newModuleDto(project)); + ComponentDto directory = componentDb.insertComponent(newDirectory(module, "dir")); + ComponentDto file = componentDb.insertComponent(newFileDto(directory)); + userSession.addProjectUuidPermissions(UserRole.USER, project.uuid()); + + ShowWsResponse response = newRequest(null, file.key()); + + assertThat(response.getComponent().getKey()).isEqualTo(file.key()); + assertThat(response.getAncestorsList()).extracting(WsComponents.Component::getKey).containsOnly(directory.key(), module.key(), project.key()); + } + + @Test + public void show_without_ancestors_when_project() throws Exception { + ComponentDto project = componentDb.insertProject(); + componentDb.insertComponent(newModuleDto(project)); + userSession.addProjectUuidPermissions(UserRole.USER, project.uuid()); + + ShowWsResponse response = newRequest(null, project.key()); + + assertThat(response.getComponent().getKey()).isEqualTo(project.key()); + assertThat(response.getAncestorsList()).isEmpty(); + } + + @Test + public void show_with_last_analysis_date() throws Exception { + ComponentDto project = componentDb.insertProject(); + componentDb.insertSnapshot(newAnalysis(project).setCreatedAt(1_000_000_000L).setLast(false)); + componentDb.insertSnapshot(newAnalysis(project).setCreatedAt(2_000_000_000L).setLast(false)); + componentDb.insertSnapshot(newAnalysis(project).setCreatedAt(3_000_000_000L).setLast(true)); + userSession.addProjectUuidPermissions(UserRole.USER, project.uuid()); + + ShowWsResponse response = newRequest(null, project.key()); + + assertThat(response.getComponent().getAnalysisDate()).isNotEmpty().isEqualTo(formatDateTime(new Date(3_000_000_000L))); + } + + @Test + public void show_with_ancestors_and_analysis_date() throws Exception { + ComponentDto project = componentDb.insertProject(); + componentDb.insertSnapshot(newAnalysis(project).setCreatedAt(3_000_000_000L).setLast(true)); + ComponentDto module = componentDb.insertComponent(newModuleDto(project)); + ComponentDto directory = componentDb.insertComponent(newDirectory(module, "dir")); + ComponentDto file = componentDb.insertComponent(newFileDto(directory)); + userSession.addProjectUuidPermissions(UserRole.USER, project.uuid()); + + ShowWsResponse response = newRequest(null, file.key()); + + String expectedDate = formatDateTime(new Date(3_000_000_000L)); + assertThat(response.getAncestorsList()).extracting(WsComponents.Component::getAnalysisDate) + .containsOnly(expectedDate, expectedDate, expectedDate); } @Test diff --git a/sonar-ws/src/main/protobuf/ws-components.proto b/sonar-ws/src/main/protobuf/ws-components.proto index d99095e075c..2c5b2673eb4 100644 --- a/sonar-ws/src/main/protobuf/ws-components.proto +++ b/sonar-ws/src/main/protobuf/ws-components.proto @@ -87,4 +87,5 @@ message Component { optional string path = 9; optional string language = 10; optional bool isFavorite = 11; + optional string analysisDate = 13; } |