diff options
10 files changed, 236 insertions, 448 deletions
diff --git a/server/sonar-server/src/main/java/org/sonar/server/project/ws/GhostsAction.java b/server/sonar-server/src/main/java/org/sonar/server/project/ws/GhostsAction.java index 5371b6be118..9f5b18afcc5 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/project/ws/GhostsAction.java +++ b/server/sonar-server/src/main/java/org/sonar/server/project/ws/GhostsAction.java @@ -32,30 +32,36 @@ import org.sonar.api.utils.text.JsonWriter; import org.sonar.api.web.UserRole; import org.sonar.db.DbClient; import org.sonar.db.DbSession; -import org.sonar.db.MyBatis; import org.sonar.db.component.ComponentDto; +import org.sonar.db.organization.OrganizationDto; import org.sonar.server.es.SearchOptions; +import org.sonar.server.organization.DefaultOrganizationProvider; import org.sonar.server.user.UserSession; import static com.google.common.collect.Sets.newHashSet; import static org.sonar.server.es.SearchOptions.MAX_LIMIT; +import static org.sonar.server.ws.WsUtils.checkFoundWithOptional; public class GhostsAction implements ProjectsWsAction { - public static final String ACTION = "ghosts"; + private static final String PARAM_ORGANIZATION = "organization"; + private static final String ACTION = "ghosts"; private static final Set<String> POSSIBLE_FIELDS = newHashSet("uuid", "key", "name", "creationDate"); private final DbClient dbClient; private final UserSession userSession; + private final DefaultOrganizationProvider defaultOrganizationProvider; - public GhostsAction(DbClient dbClient, UserSession userSession) { + public GhostsAction(DbClient dbClient, UserSession userSession, DefaultOrganizationProvider defaultOrganizationProvider) { this.dbClient = dbClient; this.userSession = userSession; + this.defaultOrganizationProvider = defaultOrganizationProvider; } @Override public void define(WebService.NewController context) { - context - .createAction(ACTION) + WebService.NewAction action = context.createAction(ACTION); + + action .setDescription("List ghost projects.<br /> Requires 'Administer System' permission.") .setResponseExample(Resources.getResource(getClass(), "projects-example-ghosts.json")) .setSince("5.2") @@ -63,30 +69,46 @@ public class GhostsAction implements ProjectsWsAction { .addFieldsParam(POSSIBLE_FIELDS) .addSearchQuery("sonar", "names", "keys") .setHandler(this); + + action.createParam(PARAM_ORGANIZATION) + .setDescription("the organization key") + .setRequired(false) + .setInternal(true) + .setSince("6.3"); } @Override public void handle(Request request, Response response) throws Exception { - userSession.checkPermission(UserRole.ADMIN); - DbSession dbSession = dbClient.openSession(false); + userSession.checkLoggedIn(); + SearchOptions searchOptions = new SearchOptions() .setPage(request.mandatoryParamAsInt(Param.PAGE), request.mandatoryParamAsInt(Param.PAGE_SIZE)); Set<String> desiredFields = fieldsToReturn(request.paramAsStrings(Param.FIELDS)); String query = request.param(Param.TEXT_QUERY); - try { - long nbOfProjects = dbClient.componentDao().countGhostProjects(dbSession, query); - List<ComponentDto> projects = dbClient.componentDao().selectGhostProjects(dbSession, searchOptions.getOffset(), searchOptions.getLimit(), query); + try (DbSession dbSession = dbClient.openSession(false)) { + OrganizationDto organization = getOrganization(dbSession, request); + userSession.checkOrganizationPermission(organization.getUuid(), UserRole.ADMIN); + + long nbOfProjects = dbClient.componentDao().countGhostProjects(dbSession, organization.getUuid(), query); + List<ComponentDto> projects = dbClient.componentDao().selectGhostProjects(dbSession, organization.getUuid(), query, + searchOptions.getOffset(), searchOptions.getLimit()); JsonWriter json = response.newJsonWriter().beginObject(); writeProjects(json, projects, desiredFields); searchOptions.writeJson(json, nbOfProjects); json.endObject().close(); - } finally { - MyBatis.closeQuietly(dbSession); } } + private OrganizationDto getOrganization(DbSession dbSession, Request request) { + String organizationKey = request.getParam(PARAM_ORGANIZATION) + .or(defaultOrganizationProvider.get()::getKey); + return checkFoundWithOptional( + dbClient.organizationDao().selectByKey(dbSession, organizationKey), + "No organization for key '%s'", organizationKey); + } + private static void writeProjects(JsonWriter json, List<ComponentDto> projects, Set<String> fieldsToReturn) { json.name("projects"); json.beginArray(); diff --git a/server/sonar-server/src/test/java/org/sonar/server/project/ws/GhostsActionTest.java b/server/sonar-server/src/test/java/org/sonar/server/project/ws/GhostsActionTest.java index 7784dd3e565..6d352a59f03 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/project/ws/GhostsActionTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/project/ws/GhostsActionTest.java @@ -20,9 +20,12 @@ package org.sonar.server.project.ws; import com.google.common.io.Resources; +import java.util.function.Consumer; import org.apache.commons.lang.StringUtils; import org.junit.Rule; import org.junit.Test; +import org.junit.rules.ExpectedException; +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.System2; @@ -35,98 +38,157 @@ import org.sonar.db.component.SnapshotDto; import org.sonar.db.component.SnapshotTesting; import org.sonar.db.organization.OrganizationDto; import org.sonar.server.exceptions.ForbiddenException; +import org.sonar.server.exceptions.NotFoundException; +import org.sonar.server.organization.DefaultOrganizationProvider; +import org.sonar.server.organization.TestDefaultOrganizationProvider; import org.sonar.server.tester.UserSessionRule; -import org.sonar.server.ws.WsTester; -import org.sonar.test.JsonAssert; +import org.sonar.server.ws.TestRequest; +import org.sonar.server.ws.TestResponse; +import org.sonar.server.ws.WsActionTester; import static org.assertj.core.api.Assertions.assertThat; +import static org.sonar.core.permission.GlobalPermissions.SYSTEM_ADMIN; +import static org.sonar.db.component.SnapshotDto.STATUS_PROCESSED; +import static org.sonar.db.component.SnapshotDto.STATUS_UNPROCESSED; +import static org.sonar.test.JsonAssert.assertJson; public class GhostsActionTest { @Rule public DbTester db = DbTester.create(System2.INSTANCE); - @Rule public UserSessionRule userSessionRule = UserSessionRule.standalone(); + @Rule + public ExpectedException expectedException = ExpectedException.none(); - DbClient dbClient = db.getDbClient(); - WsTester ws = new WsTester(new ProjectsWs(new GhostsAction(dbClient, userSessionRule))); + private DefaultOrganizationProvider defaultOrganizationProvider = TestDefaultOrganizationProvider.from(db); + private DbClient dbClient = db.getDbClient(); + private WsActionTester underTest = new WsActionTester(new GhostsAction(dbClient, userSessionRule, defaultOrganizationProvider)); + + @Test + public void verify_definition() { + WebService.Action action = underTest.getDef(); + assertThat(action.description()).isEqualTo("List ghost projects.<br /> Requires 'Administer System' permission."); + assertThat(action.since()).isEqualTo("5.2"); + assertThat(action.isInternal()).isFalse(); + + assertThat(action.params()).hasSize(5); + + Param organization = action.param("organization"); + assertThat(organization.description()).isEqualTo("the organization key"); + assertThat(organization.since()).isEqualTo("6.3"); + assertThat(organization.isRequired()).isFalse(); + assertThat(organization.isInternal()).isTrue(); + } @Test public void ghost_projects_without_analyzed_projects() throws Exception { - userSessionRule.setGlobalPermissions(UserRole.ADMIN); - insertNewGhostProject("1"); - insertNewGhostProject("2"); - insertNewActiveProject("3"); + OrganizationDto organization = db.organizations().insert(); + ComponentDto ghost1 = insertGhostProject(organization); + ComponentDto ghost2 = insertGhostProject(organization); + ComponentDto activeProject = insertActiveProject(organization); + userSessionRule.login().addOrganizationPermission(organization, SYSTEM_ADMIN); - WsTester.Result result = ws.newGetRequest("api/projects", "ghosts").execute(); + TestResponse result = underTest.newRequest() + .setParam("organization", organization.getKey()) + .execute(); - result.assertJson(getClass(), "all-projects.json"); - assertThat(result.outputAsString()).doesNotContain("analyzed-uuid-3"); + String json = result.getInput(); + assertJson(json).isSimilarTo("{" + + " \"projects\": [" + + " {" + + " \"uuid\": \"" + ghost1.uuid() + "\"," + + " \"key\": \"" + ghost1.key() + "\"," + + " \"name\": \"" + ghost1.name() + "\"" + + " }," + + " {" + + " \"uuid\": \"" + ghost2.uuid() + "\"," + + " \"key\": \"" + ghost2.key() + "\"," + + " \"name\": \"" + ghost2.name() + "\"" + + " }" + + " ]" + + "}"); + assertThat(json).doesNotContain(activeProject.uuid()); } @Test public void ghost_projects_with_correct_pagination() throws Exception { - userSessionRule.setGlobalPermissions(UserRole.ADMIN); + OrganizationDto organization = db.organizations().insert(); for (int i = 1; i <= 10; i++) { - insertNewGhostProject(String.valueOf(i)); + int count = i; + insertGhostProject(organization, dto -> dto.setKey("ghost-key-" + count)); } + userSessionRule.login().addOrganizationPermission(organization, SYSTEM_ADMIN); - WsTester.Result result = ws.newGetRequest("api/projects", "ghosts") + TestResponse result = underTest.newRequest() + .setParam("organization", organization.getKey()) .setParam(Param.PAGE, "3") .setParam(Param.PAGE_SIZE, "4") .execute(); - result.assertJson(getClass(), "pagination.json"); - assertThat(StringUtils.countMatches(result.outputAsString(), "ghost-uuid-")).isEqualTo(2); + String json = result.getInput(); + assertJson(json).isSimilarTo("{" + + " \"p\": 3," + + " \"ps\": 4," + + " \"total\": 10" + + "}"); + assertThat(StringUtils.countMatches(json, "ghost-key-")).isEqualTo(2); } @Test public void ghost_projects_with_chosen_fields() throws Exception { - userSessionRule.setGlobalPermissions(UserRole.ADMIN); - insertNewGhostProject("1"); + OrganizationDto organization = db.organizations().insert(); + insertGhostProject(organization); + userSessionRule.login().addOrganizationPermission(organization, SYSTEM_ADMIN); - WsTester.Result result = ws.newGetRequest("api/projects", "ghosts") + TestResponse result = underTest.newRequest() + .setParam("organization", organization.getKey()) .setParam(Param.FIELDS, "name") .execute(); - assertThat(result.outputAsString()).contains("uuid", "name") + assertThat(result.getInput()) + .contains("uuid", "name") .doesNotContain("key") .doesNotContain("creationDate"); } @Test public void ghost_projects_with_partial_query_on_name() throws Exception { - userSessionRule.setGlobalPermissions(UserRole.ADMIN); + OrganizationDto organization = db.organizations().insert(); + insertGhostProject(organization, dto -> dto.setName("ghost-name-10")); + insertGhostProject(organization, dto -> dto.setName("ghost-name-11")); + insertGhostProject(organization, dto -> dto.setName("ghost-name-20")); - insertNewGhostProject("10"); - insertNewGhostProject("11"); - insertNewGhostProject("2"); + userSessionRule.login().addOrganizationPermission(organization, SYSTEM_ADMIN); - WsTester.Result result = ws.newGetRequest("api/projects", "ghosts") + TestResponse result = underTest.newRequest() + .setParam("organization", organization.getKey()) .setParam(Param.TEXT_QUERY, "name-1") .execute(); - assertThat(result.outputAsString()).contains("ghost-name-10", "ghost-name-11") + assertThat(result.getInput()) + .contains("ghost-name-10", "ghost-name-11") .doesNotContain("ghost-name-2"); } @Test public void ghost_projects_with_partial_query_on_key() throws Exception { - userSessionRule.setGlobalPermissions(UserRole.ADMIN); + OrganizationDto organization = db.organizations().insert(); + insertGhostProject(organization, dto -> dto.setKey("ghost-key-1")); - insertNewGhostProject("1"); + userSessionRule.login().addOrganizationPermission(organization, SYSTEM_ADMIN); - WsTester.Result result = ws.newGetRequest("api/projects", "ghosts") + TestResponse result = underTest.newRequest() + .setParam("organization", organization.getKey()) .setParam(Param.TEXT_QUERY, "GHOST-key") .execute(); - assertThat(result.outputAsString()).contains("ghost-key-1"); + assertThat(result.getInput()) + .contains("ghost-key-1"); } @Test public void ghost_projects_base_on_json_example() throws Exception { - userSessionRule.setGlobalPermissions(UserRole.ADMIN); OrganizationDto organizationDto = db.organizations().insert(); ComponentDto hBaseProject = ComponentTesting.newProjectDto(organizationDto, "ce4c03d6-430f-40a9-b777-ad877c00aa4d") .setKey("org.apache.hbas:hbase") @@ -134,38 +196,62 @@ public class GhostsActionTest { .setCreatedAt(DateUtils.parseDateTime("2015-03-04T23:03:44+0100")); dbClient.componentDao().insert(db.getSession(), hBaseProject); dbClient.snapshotDao().insert(db.getSession(), SnapshotTesting.newAnalysis(hBaseProject) - .setStatus(SnapshotDto.STATUS_UNPROCESSED)); + .setStatus(STATUS_UNPROCESSED)); ComponentDto roslynProject = ComponentTesting.newProjectDto(organizationDto, "c526ef20-131b-4486-9357-063fa64b5079") .setKey("com.microsoft.roslyn:roslyn") .setName("Roslyn") .setCreatedAt(DateUtils.parseDateTime("2013-03-04T23:03:44+0100")); dbClient.componentDao().insert(db.getSession(), roslynProject); dbClient.snapshotDao().insert(db.getSession(), SnapshotTesting.newAnalysis(roslynProject) - .setStatus(SnapshotDto.STATUS_UNPROCESSED)); + .setStatus(STATUS_UNPROCESSED)); db.getSession().commit(); + userSessionRule.login().addOrganizationPermission(organizationDto, SYSTEM_ADMIN); - WsTester.Result result = ws.newGetRequest("api/projects", "ghosts").execute(); + TestResponse result = underTest.newRequest() + .setParam("organization", organizationDto.getKey()) + .execute(); - JsonAssert.assertJson(result.outputAsString()).isSimilarTo(Resources.getResource(getClass(), "projects-example-ghosts.json")); + assertJson(result.getInput()) + .isSimilarTo(Resources.getResource(getClass(), "projects-example-ghosts.json")); } @Test(expected = ForbiddenException.class) public void fail_if_does_not_have_sufficient_rights() throws Exception { - userSessionRule.setGlobalPermissions(UserRole.USER, UserRole.ISSUE_ADMIN, UserRole.CODEVIEWER); + userSessionRule.login() + .addOrganizationPermission(db.getDefaultOrganization(), UserRole.USER) + .addOrganizationPermission(db.getDefaultOrganization(), UserRole.ISSUE_ADMIN) + .addOrganizationPermission(db.getDefaultOrganization(), UserRole.CODEVIEWER); - ws.newGetRequest("api/projects", "ghosts").execute(); + underTest.newRequest().execute(); } - private void insertNewGhostProject(String id) { - ComponentDto project = ComponentTesting - .newProjectDto(db.organizations().insert(), "ghost-uuid-" + id) - .setName("ghost-name-" + id) - .setKey("ghost-key-" + id); - dbClient.componentDao().insert(db.getSession(), project); - SnapshotDto snapshot = SnapshotTesting.newAnalysis(project) - .setStatus(SnapshotDto.STATUS_UNPROCESSED); - dbClient.snapshotDao().insert(db.getSession(), snapshot); - db.getSession().commit(); + @Test + public void fail_with_NotFoundException_when_organization_with_specified_key_does_not_exist() { + TestRequest request = underTest.newRequest() + .setParam("organization", "foo"); + userSessionRule.login(); + + expectedException.expect(NotFoundException.class); + expectedException.expectMessage("No organization for key 'foo'"); + + request.execute(); + } + + private ComponentDto insertGhostProject(OrganizationDto organization) { + return insertGhostProject(organization, dto -> { + }); + } + + private ComponentDto insertGhostProject(OrganizationDto organization, Consumer<ComponentDto> consumer) { + ComponentDto project = db.components().insertProject(organization, consumer); + db.components().insertSnapshot(project, dto -> dto.setStatus(STATUS_UNPROCESSED)); + return project; + } + + private ComponentDto insertActiveProject(OrganizationDto organization) { + ComponentDto project = db.components().insertProject(organization); + db.components().insertSnapshot(project, dto -> dto.setStatus(STATUS_PROCESSED)); + return project; } private void insertNewActiveProject(String id) { diff --git a/server/sonar-server/src/test/resources/org/sonar/server/project/ws/GhostsActionTest/all-projects.json b/server/sonar-server/src/test/resources/org/sonar/server/project/ws/GhostsActionTest/all-projects.json deleted file mode 100644 index cd1aad1fa60..00000000000 --- a/server/sonar-server/src/test/resources/org/sonar/server/project/ws/GhostsActionTest/all-projects.json +++ /dev/null @@ -1,14 +0,0 @@ -{ - "projects": [ - { - "uuid": "ghost-uuid-1", - "key": "ghost-key-1", - "name": "ghost-name-1" - }, - { - "uuid": "ghost-uuid-2", - "key": "ghost-key-2", - "name": "ghost-name-2" - } - ] -} diff --git a/server/sonar-server/src/test/resources/org/sonar/server/project/ws/GhostsActionTest/pagination.json b/server/sonar-server/src/test/resources/org/sonar/server/project/ws/GhostsActionTest/pagination.json deleted file mode 100644 index 8967d5cb758..00000000000 --- a/server/sonar-server/src/test/resources/org/sonar/server/project/ws/GhostsActionTest/pagination.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "p": 3, - "ps": 4, - "total": 10 -} diff --git a/sonar-db/src/main/java/org/sonar/db/component/ComponentDao.java b/sonar-db/src/main/java/org/sonar/db/component/ComponentDao.java index d475e9ead20..7deb692a59a 100644 --- a/sonar-db/src/main/java/org/sonar/db/component/ComponentDao.java +++ b/sonar-db/src/main/java/org/sonar/db/component/ComponentDao.java @@ -41,7 +41,6 @@ import org.sonar.db.DbSession; import org.sonar.db.RowNotFoundException; import static com.google.common.base.Preconditions.checkArgument; -import static com.google.common.collect.Maps.newHashMapWithExpectedSize; import static java.util.Collections.emptyList; import static java.util.Objects.requireNonNull; import static org.apache.commons.lang.StringUtils.isBlank; @@ -263,20 +262,12 @@ public class ComponentDao implements Dao { return DatabaseUtils.buildLikeValue(textQuery.toUpperCase(Locale.ENGLISH), BEFORE_AND_AFTER); } - public List<ComponentDto> selectGhostProjects(DbSession session, int offset, int limit, @Nullable String query) { - Map<String, Object> parameters = newHashMapWithExpectedSize(2); - addProjectQualifier(parameters); - addPartialQueryParameterIfNotNull(parameters, query); - - return mapper(session).selectGhostProjects(parameters, new RowBounds(offset, limit)); + public List<ComponentDto> selectGhostProjects(DbSession session, String organizationUuid, @Nullable String query, int offset, int limit) { + return mapper(session).selectGhostProjects(organizationUuid, queryParameterFrom(query), new RowBounds(offset, limit)); } - public long countGhostProjects(DbSession session, @Nullable String query) { - Map<String, Object> parameters = newHashMapWithExpectedSize(2); - addProjectQualifier(parameters); - addPartialQueryParameterIfNotNull(parameters, query); - - return mapper(session).countGhostProjects(parameters); + public long countGhostProjects(DbSession session, String organizationUuid, @Nullable String query) { + return mapper(session).countGhostProjects(organizationUuid, queryParameterFrom(query)); } /** @@ -317,12 +308,16 @@ public class ComponentDao implements Dao { private static void addPartialQueryParameterIfNotNull(Map<String, Object> parameters, @Nullable String keyOrNameFilter) { // TODO rely on resource_index table and match exactly the key if (keyOrNameFilter != null) { - parameters.put("query", "%" + keyOrNameFilter.toUpperCase(Locale.ENGLISH) + "%"); + parameters.put("query", queryParameterFrom(keyOrNameFilter)); } } - private static void addProjectQualifier(Map<String, Object> parameters) { - parameters.put("qualifier", Qualifiers.PROJECT); + @CheckForNull + private static String queryParameterFrom(@Nullable String keyOrNameFilter) { + if (keyOrNameFilter != null) { + return "%" + keyOrNameFilter.toUpperCase(Locale.ENGLISH) + "%"; + } + return null; } public void insert(DbSession session, ComponentDto item) { diff --git a/sonar-db/src/main/java/org/sonar/db/component/ComponentMapper.java b/sonar-db/src/main/java/org/sonar/db/component/ComponentMapper.java index 2cddadffbd4..23bda21a9e9 100644 --- a/sonar-db/src/main/java/org/sonar/db/component/ComponentMapper.java +++ b/sonar-db/src/main/java/org/sonar/db/component/ComponentMapper.java @@ -21,7 +21,6 @@ package org.sonar.db.component; import java.util.Collection; import java.util.List; -import java.util.Map; import java.util.Set; import javax.annotation.CheckForNull; import javax.annotation.Nullable; @@ -122,14 +121,14 @@ public interface ComponentMapper { int countProvisioned(@Param("organizationUuid") String organizationUuid, @Nullable @Param("keyOrNameLike") String keyOrNameLike, @Param("qualifiers") Set<String> qualifiers); - List<ComponentDto> selectGhostProjects(Map<String, Object> parameters, RowBounds rowBounds); + List<ComponentDto> selectGhostProjects(@Param("organizationUuid") String organizationUuid, @Nullable @Param("query") String query, RowBounds rowBounds); + + long countGhostProjects(@Param("organizationUuid") String organizationUuid, @Nullable @Param("query") String query); List<ComponentDto> selectComponentsHavingSameKeyOrderedById(String key); List<ComponentDto> selectProjectsByNameQuery(@Param("nameQuery") @Nullable String nameQuery, @Param("includeModules") boolean includeModules); - long countGhostProjects(Map<String, Object> parameters); - void selectForIndexing(@Param("projectUuid") @Nullable String projectUuid, ResultHandler handler); void insert(ComponentDto componentDto); diff --git a/sonar-db/src/main/resources/org/sonar/db/component/ComponentMapper.xml b/sonar-db/src/main/resources/org/sonar/db/component/ComponentMapper.xml index b8a97b3df49..79b19813fff 100644 --- a/sonar-db/src/main/resources/org/sonar/db/component/ComponentMapper.xml +++ b/sonar-db/src/main/resources/org/sonar/db/component/ComponentMapper.xml @@ -380,23 +380,24 @@ <select id="selectGhostProjects" parameterType="map" resultType="Component"> select distinct - <include refid="componentColumns"/> + <include refid="componentColumns"/> from projects p - <include refid="ghostClauses"/> + <include refid="ghostProjectClauses"/> </select> <select id="countGhostProjects" parameterType="map" resultType="long"> - select count(p.id) + select + count(distinct p.id) from projects p - <include refid="ghostClauses"/> + <include refid="ghostProjectClauses"/> </select> - <sql id="ghostClauses"> + <sql id="ghostProjectClauses"> inner join snapshots s1 on s1.component_uuid = p.uuid and s1.status='U' left join snapshots s2 on s2.component_uuid = p.uuid and s2.status='P' where s2.id is null - and p.qualifier=#{qualifier,jdbcType=VARCHAR} + and p.qualifier='TRK' and p.copy_component_uuid is null <if test="query!=null"> and ( diff --git a/sonar-db/src/test/java/org/sonar/db/component/ComponentDaoTest.java b/sonar-db/src/test/java/org/sonar/db/component/ComponentDaoTest.java index 646a54be31a..2ac7254b0ce 100644 --- a/sonar-db/src/test/java/org/sonar/db/component/ComponentDaoTest.java +++ b/sonar-db/src/test/java/org/sonar/db/component/ComponentDaoTest.java @@ -49,6 +49,7 @@ import static java.util.Collections.singletonList; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.guava.api.Assertions.assertThat; import static org.sonar.db.component.ComponentTesting.newDeveloper; +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.newProjectCopy; @@ -596,13 +597,39 @@ public class ComponentDaoTest { @Test public void select_ghost_projects() { - db.prepareDbUnit(getClass(), "select_ghost_projects.xml"); - - List<ComponentDto> result = underTest.selectGhostProjects(dbSession, 0, 10, null); + OrganizationDto organization = db.organizations().insert(); - assertThat(result).hasSize(1); - assertThat(result.get(0).key()).isEqualTo("org.ghost.project"); - assertThat(underTest.countGhostProjects(dbSession, null)).isEqualTo(1); + // ghosts because has at least one snapshot with status U but none with status P + ComponentDto ghostProject = db.components().insertProject(organization); + db.components().insertSnapshot(ghostProject, dto -> dto.setStatus("U")); + db.components().insertSnapshot(ghostProject, dto -> dto.setStatus("U")); + ComponentDto ghostProject2 = db.components().insertProject(organization); + db.components().insertSnapshot(ghostProject2, dto -> dto.setStatus("U")); + ComponentDto disabledGhostProject = db.components().insertProject(dto -> dto.setEnabled(false)); + db.components().insertSnapshot(disabledGhostProject, dto -> dto.setStatus("U")); + + ComponentDto project1 = db.components().insertProject(organization); + db.components().insertSnapshot(project1, dto -> dto.setStatus("P")); + db.components().insertSnapshot(project1, dto -> dto.setStatus("U")); + ComponentDto module = db.components().insertComponent(newModuleDto(project1)); + ComponentDto dir = db.components().insertComponent(newDirectory(module, "foo")); + db.components().insertComponent(newFileDto(module, dir, "bar")); + + ComponentDto provisionedProject = db.components().insertProject(organization); + + // not a ghost because has at least one snapshot with status P + ComponentDto project2 = db.components().insertProject(organization); + db.components().insertSnapshot(project2, dto -> dto.setStatus("P")); + + // not a ghost because it's not a project + ComponentDto view = db.components().insertView(organization); + db.components().insertSnapshot(view, dto -> dto.setStatus("U")); + db.components().insertComponent(newProjectCopy("do", project1, view)); + + assertThat(underTest.selectGhostProjects(dbSession, organization.getUuid(), null, 0, 10)) + .extracting(ComponentDto::uuid) + .containsOnly(ghostProject.uuid(), ghostProject2.uuid(), disabledGhostProject.uuid()); + assertThat(underTest.countGhostProjects(dbSession, organization.getUuid(), null)).isEqualTo(3); } @Test diff --git a/sonar-db/src/test/java/org/sonar/db/component/ComponentDbTester.java b/sonar-db/src/test/java/org/sonar/db/component/ComponentDbTester.java index 67d438cb847..d8f93680b66 100644 --- a/sonar-db/src/test/java/org/sonar/db/component/ComponentDbTester.java +++ b/sonar-db/src/test/java/org/sonar/db/component/ComponentDbTester.java @@ -95,7 +95,7 @@ public class ComponentDbTester { } public ComponentDto insertView(OrganizationDto organizationDto) { - return insertComponentImpl(newView(organizationDto), dto -> {}); + return insertComponentImpl(newView(organizationDto), noExtraConfiguration()); } public ComponentDto insertView(OrganizationDto organizationDto, Consumer<ComponentDto> dtoPopulator) { @@ -111,7 +111,7 @@ public class ComponentDbTester { } public ComponentDto insertDeveloper(String name, Consumer<ComponentDto> dtoPopulator) { - return insertComponentImpl(newDeveloper(db.getDefaultOrganization(), name), noExtraConfiguration()); + return insertComponentImpl(newDeveloper(db.getDefaultOrganization(), name), dtoPopulator); } public ComponentDto insertDeveloper(String name) { @@ -145,4 +145,16 @@ public class ComponentDbTester { db.commit(); return snapshot; } + + public SnapshotDto insertSnapshot(ComponentDto componentDto) { + return insertSnapshot(componentDto, noExtraConfiguration()); + } + + public SnapshotDto insertSnapshot(ComponentDto componentDto, Consumer<SnapshotDto> consumer) { + SnapshotDto snapshotDto = SnapshotTesting.newAnalysis(componentDto); + consumer.accept(snapshotDto); + SnapshotDto snapshot = dbClient.snapshotDao().insert(dbSession, snapshotDto); + db.commit(); + return snapshot; + } } diff --git a/sonar-db/src/test/resources/org/sonar/db/component/ComponentDaoTest/select_ghost_projects.xml b/sonar-db/src/test/resources/org/sonar/db/component/ComponentDaoTest/select_ghost_projects.xml deleted file mode 100644 index f5ad3efa9c2..00000000000 --- a/sonar-db/src/test/resources/org/sonar/db/component/ComponentDaoTest/select_ghost_projects.xml +++ /dev/null @@ -1,335 +0,0 @@ -<dataset> - - <!-- Struts projects is authorized for all user --> - <group_roles id="1" - group_id="[null]" - resource_id="1" - role="user" - organization_uuid="org1"/> - - <!-- Ghost project --> - <projects organization_uuid="org1" - id="42" - root_uuid="PPAA" - scope="PRJ" - qualifier="TRK" - kee="org.ghost.project" - name="Ghost Project" - uuid="PPAA" - uuid_path="NOT_USED" - project_uuid="PPAA" - module_uuid="[null]" - module_uuid_path="." - description="the description" - long_name="Ghost Project" - enabled="[true]" - language="[null]" - copy_component_uuid="[null]" - developer_uuid="[null]" - path="[null]" - authorization_updated_at="123456789"/> - - <!-- root project --> - <projects organization_uuid="org1" - id="1" - root_uuid="ABCD" - scope="PRJ" - qualifier="TRK" - kee="org.struts:struts" - deprecated_kee="org.struts:struts" - name="Struts" - uuid="ABCD" - uuid_path="NOT_USED" - project_uuid="ABCD" - module_uuid="[null]" - module_uuid_path=".ABCD." - description="the description" - long_name="Apache Struts" - enabled="[true]" - language="[null]" - copy_component_uuid="[null]" - developer_uuid="[null]" - path="[null]" - authorization_updated_at="123456789"/> - <snapshots id="1" - uuid="u1" - component_uuid="ABCD" - status="P" - islast="[true]" - purge_status="[null]" - period1_mode="[null]" - period1_param="[null]" - period1_date="[null]" - period2_mode="[null]" - period2_param="[null]" - period2_date="[null]" - period3_mode="[null]" - period3_param="[null]" - period3_date="[null]" - period4_mode="[null]" - period4_param="[null]" - period4_date="[null]" - period5_mode="[null]" - period5_param="[null]" - period5_date="[null]" - created_at="1228222680000" - build_date="1228222680000" - version="[null]" - /> - <snapshots id="10" - uuid="u10" - component_uuid="ABCD" - status="P" - islast="[false]" - purge_status="[null]" - period1_mode="[null]" - period1_param="[null]" - period1_date="[null]" - period2_mode="[null]" - period2_param="[null]" - period2_date="[null]" - period3_mode="[null]" - period3_param="[null]" - period3_date="[null]" - period4_mode="[null]" - period4_param="[null]" - period4_date="[null]" - period5_mode="[null]" - period5_param="[null]" - period5_date="[null]" - created_at="1228136280000" - build_date="1228136280000" - version="[null]" - /> - <snapshots id="11" - uuid="u11" - component_uuid="PPAA" - status="U" - islast="[false]" - purge_status="[null]" - period1_mode="[null]" - period1_param="[null]" - period1_date="[null]" - period2_mode="[null]" - period2_param="[null]" - period2_date="[null]" - period3_mode="[null]" - period3_param="[null]" - period3_date="[null]" - period4_mode="[null]" - period4_param="[null]" - period4_date="[null]" - period5_mode="[null]" - period5_param="[null]" - period5_date="[null]" - created_at="1228136280000" - build_date="1228136280000" - version="[null]" - /> - - <!-- module --> - <projects organization_uuid="org1" - id="2" - root_uuid="ABCD" - kee="org.struts:struts-core" - name="Struts Core" - uuid="EFGH" - uuid_path="NOT_USED" - project_uuid="ABCD" - module_uuid="[null]" - module_uuid_path=".ABCD.EFGH." - scope="PRJ" - qualifier="BRC" - long_name="Struts Core" - description="[null]" - enabled="[true]" - language="[null]" - copy_component_uuid="[null]" - developer_uuid="[null]" - authorization_updated_at="[null]"/> - <snapshots id="2" - uuid="u2" - component_uuid="EFGH" - status="P" - islast="[true]" - purge_status="[null]" - period1_mode="[null]" - period1_param="[null]" - period1_date="[null]" - period2_mode="[null]" - period2_param="[null]" - period2_date="[null]" - period3_mode="[null]" - period3_param="[null]" - period3_date="[null]" - period4_mode="[null]" - period4_param="[null]" - period4_date="[null]" - period5_mode="[null]" - period5_param="[null]" - period5_date="[null]" - created_at="1228222680000" - build_date="1228222680000" - version="[null]" - /> - - <!-- directory --> - <projects organization_uuid="org1" - long_name="org.struts" - id="3" - scope="DIR" - qualifier="DIR" - kee="org.struts:struts-core:src/org/struts" - uuid="GHIJ" - uuid_path="NOT_USED" - project_uuid="ABCD" - module_uuid="EFGH" - module_uuid_path=".ABCD.EFGH." - name="src/org/struts" - root_uuid="EFGH" - description="[null]" - enabled="[true]" - language="[null]" - copy_component_uuid="[null]" - developer_uuid="[null]" - path="src/org/struts" - authorization_updated_at="[null]"/> - <snapshots id="3" - uuid="u3" - component_uuid="GHIJ" - status="P" - islast="[true]" - purge_status="[null]" - period1_mode="[null]" - period1_param="[null]" - period1_date="[null]" - period2_mode="[null]" - period2_param="[null]" - period2_date="[null]" - period3_mode="[null]" - period3_param="[null]" - period3_date="[null]" - period4_mode="[null]" - period4_param="[null]" - period4_date="[null]" - period5_mode="[null]" - period5_param="[null]" - period5_date="[null]" - created_at="1228222680000" - build_date="1228222680000" - version="[null]" - /> - - <!-- file --> - <projects organization_uuid="org1" - long_name="org.struts.RequestContext" - id="4" - scope="FIL" - qualifier="FIL" - kee="org.struts:struts-core:src/org/struts/RequestContext.java" - uuid="KLMN" - uuid_path="NOT_USED" - project_uuid="ABCD" - module_uuid="EFGH" - module_uuid_path=".ABCD.EFGH." - name="RequestContext.java" - root_uuid="EFGH" - description="[null]" - enabled="[true]" - language="java" - copy_component_uuid="[null]" - developer_uuid="[null]" - path="src/org/struts/RequestContext.java" - authorization_updated_at="[null]"/> - - <snapshots id="4" - uuid="u4" - component_uuid="KLMN" - status="P" - islast="[true]" - purge_status="[null]" - period1_mode="[null]" - period1_param="[null]" - period1_date="[null]" - period2_mode="[null]" - period2_param="[null]" - period2_date="[null]" - period3_mode="[null]" - period3_param="[null]" - period3_date="[null]" - period4_mode="[null]" - period4_param="[null]" - period4_date="[null]" - period5_mode="[null]" - period5_param="[null]" - period5_date="[null]" - created_at="1228222680000" - build_date="1228222680000" - version="[null]" - /> - - <!-- Disabled projects --> - <projects organization_uuid="org1" - id="10" - root_uuid="DCBA" - scope="PRJ" - qualifier="TRK" - kee="org.disabled.project" - name="Disabled Project" - uuid="DCBA" - uuid_path="NOT_USED" - project_uuid="DCBA" - module_uuid="[null]" - module_uuid_path="." - description="the description" - long_name="Disabled project" - enabled="[false]" - language="[null]" - copy_component_uuid="[null]" - developer_uuid="[null]" - path="[null]" - authorization_updated_at="123456789"/> - - <!-- Developer and technical project copy --> - <projects organization_uuid="org1" - id="11" - root_uuid="OPQR" - scope="PRJ" - qualifier="DEV" - kee="DEV:anakin@skywalker.name" - name="Anakin Skywalker" - uuid="OPQR" - uuid_path="NOT_USED" - project_uuid="OPQR" - module_uuid="[null]" - module_uuid_path=".OPQR." - description="the description" - long_name="Anakin Skywalker" - enabled="[true]" - language="[null]" - copy_component_uuid="[null]" - developer_uuid="[null]" - path="[null]" - authorization_updated_at="123456789"/> - <projects organization_uuid="org1" - id="12" - root_uuid="OPQR" - scope="PRJ" - qualifier="DEV_PRJ" - kee="DEV:anakin@skywalker.name:org.struts:struts" - name="Apache Struts" - uuid="STUV" - uuid_path="NOT_USED" - project_uuid="OPQR" - module_uuid="OPQR" - module_uuid_path=".OPQR." - description="the description" - long_name="Apache Struts" - enabled="[true]" - language="[null]" - copy_component_uuid="ABCD" - developer_uuid="OPQR" - path="[null]" - authorization_updated_at="123456789"/> - -</dataset> |