Browse Source

WS api/projects/ghosts list ghost projects - SONAR-6527

tags/5.2-RC1
Teryk Bellahsene 9 years ago
parent
commit
e2a24d90b1
17 changed files with 598 additions and 43 deletions
  1. 35
    12
      server/sonar-server/src/main/java/org/sonar/server/component/db/ComponentDao.java
  2. 125
    0
      server/sonar-server/src/main/java/org/sonar/server/component/ws/ProjectsGhostsAction.java
  3. 16
    18
      server/sonar-server/src/main/java/org/sonar/server/component/ws/ProjectsProvisionedAction.java
  4. 3
    1
      server/sonar-server/src/main/java/org/sonar/server/component/ws/ProjectsWs.java
  5. 4
    2
      server/sonar-server/src/main/java/org/sonar/server/platform/ServerComponents.java
  6. 19
    0
      server/sonar-server/src/main/resources/org/sonar/server/component/ws/projects-example-ghosts.json
  7. 11
    0
      server/sonar-server/src/test/java/org/sonar/server/component/db/ComponentDaoTest.java
  8. 199
    0
      server/sonar-server/src/test/java/org/sonar/server/component/ws/ProjectsGhostsActionTest.java
  9. 7
    6
      server/sonar-server/src/test/java/org/sonar/server/component/ws/ProjectsProvisionedActionTest.java
  10. 3
    2
      server/sonar-server/src/test/java/org/sonar/server/component/ws/ProjectsWsTest.java
  11. 109
    0
      server/sonar-server/src/test/resources/org/sonar/server/component/db/ComponentDaoTest/select_ghost_projects.xml
  12. 14
    0
      server/sonar-server/src/test/resources/org/sonar/server/component/ws/ProjectsGhostsActionTest/all-projects.json
  13. 5
    0
      server/sonar-server/src/test/resources/org/sonar/server/component/ws/ProjectsGhostsActionTest/pagination.json
  14. 0
    0
      server/sonar-server/src/test/resources/org/sonar/server/component/ws/ProjectsProvisionedActionTest/all-projects.json
  15. 4
    0
      sonar-core/src/main/java/org/sonar/core/component/db/ComponentMapper.java
  16. 29
    2
      sonar-core/src/main/resources/org/sonar/core/component/db/ComponentMapper.xml
  17. 15
    0
      sonar-plugin-api/src/main/java/org/sonar/api/server/ws/WebService.java

+ 35
- 12
server/sonar-server/src/main/java/org/sonar/server/component/db/ComponentDao.java View File

@@ -22,7 +22,6 @@ package org.sonar.server.component.db;

import com.google.common.base.Function;
import org.apache.ibatis.session.RowBounds;
import org.sonar.api.ServerComponent;
import org.sonar.api.ServerSide;
import org.sonar.api.resources.Qualifiers;
import org.sonar.api.resources.Scopes;
@@ -40,13 +39,13 @@ import org.sonar.server.exceptions.NotFoundException;

import javax.annotation.CheckForNull;
import javax.annotation.Nullable;

import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import static com.google.common.collect.Maps.newHashMapWithExpectedSize;

/**
* @since 4.3
*/
@@ -176,20 +175,44 @@ public class ComponentDao extends BaseDao<ComponentMapper, ComponentDto, String>
}

public List<ComponentDto> selectProvisionedProjects(DbSession session, SearchOptions searchOptions, @Nullable String query) {
Map<String, String> parameters = new HashMap<>();
parameters.put("qualifier", Qualifiers.PROJECT);
if (query != null) {
parameters.put("query", "%" + query + "%");
}
Map<String, String> parameters = newHashMapWithExpectedSize(2);
addProjectQualifier(parameters);
addPartialQueryParameterIfNotNull(parameters, query);

return mapper(session).selectProvisionedProjects(parameters, new RowBounds(searchOptions.getOffset(), searchOptions.getLimit()));
}

public int countProvisionedProjects(DbSession session, @Nullable String query) {
Map<String, String> parameters = new HashMap<>();
parameters.put("qualifier", Qualifiers.PROJECT);
Map<String, String> parameters = newHashMapWithExpectedSize(2);
addProjectQualifier(parameters);
addPartialQueryParameterIfNotNull(parameters, query);

return mapper(session).countProvisionedProjects(parameters);
}

public List<ComponentDto> selectGhostProjects(DbSession session, @Nullable String query, SearchOptions options) {
Map<String, String> parameters = newHashMapWithExpectedSize(2);
addProjectQualifier(parameters);
addPartialQueryParameterIfNotNull(parameters, query);

return mapper(session).selectGhostProjects(parameters, new RowBounds(options.getOffset(), options.getLimit()));
}

public long countGhostProjects(DbSession session, @Nullable String query) {
Map<String, String> parameters = newHashMapWithExpectedSize(2);
addProjectQualifier(parameters);
addPartialQueryParameterIfNotNull(parameters, query);

return mapper(session).countGhostProjects(parameters);
}

private void addPartialQueryParameterIfNotNull(Map<String, String> parameters, @Nullable String query) {
if (query != null) {
parameters.put("query", "%" + query + "%");
parameters.put("query", "%" + query.toUpperCase() + "%");
}
return mapper(session).countProvisionedProjects(parameters);
}

private void addProjectQualifier(Map<String, String> parameters) {
parameters.put("qualifier", Qualifiers.PROJECT);
}
}

+ 125
- 0
server/sonar-server/src/main/java/org/sonar/server/component/ws/ProjectsGhostsAction.java View File

@@ -0,0 +1,125 @@
/*
* SonarQube, open source software quality management tool.
* Copyright (C) 2008-2014 SonarSource
* mailto:contact AT sonarsource DOT com
*
* SonarQube 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.
*
* SonarQube 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.sonar.server.component.ws;

import com.google.common.io.Resources;
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.text.JsonWriter;
import org.sonar.api.web.UserRole;
import org.sonar.core.component.ComponentDto;
import org.sonar.core.persistence.DbSession;
import org.sonar.core.persistence.MyBatis;
import org.sonar.server.db.DbClient;
import org.sonar.server.es.SearchOptions;
import org.sonar.server.user.UserSession;

import javax.annotation.Nullable;

import java.util.Date;
import java.util.List;
import java.util.Set;

import static com.google.common.collect.Sets.newHashSet;

public class ProjectsGhostsAction implements ProjectsWsAction {
public 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;

public ProjectsGhostsAction(DbClient dbClient, UserSession userSession) {
this.dbClient = dbClient;
this.userSession = userSession;
}

@Override
public void define(WebService.NewController context) {
context
.createAction(ACTION)
.setDescription("List ghost projects.<br /> Requires admin role.")
.setResponseExample(Resources.getResource(getClass(), "projects-example-ghosts.json"))
.setSince("5.2")
.addPagingParams(100)
.addFieldsParam(POSSIBLE_FIELDS)
.addSearchQuery("sonar", "names", "keys")
.setHandler(this);
}

@Override
public void handle(Request request, Response response) throws Exception {
userSession.checkGlobalPermission(UserRole.ADMIN);
DbSession dbSession = dbClient.openSession(false);
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, query, searchOptions);
JsonWriter json = response.newJsonWriter().beginObject();
writeProjects(json, projects, desiredFields);
searchOptions.writeJson(json, nbOfProjects);
json.endObject().close();
} finally {
MyBatis.closeQuietly(dbSession);
}
}

private void writeProjects(JsonWriter json, List<ComponentDto> projects, Set<String> fieldsToReturn) {
json.name("projects");
json.beginArray();
for (ComponentDto project : projects) {
json.beginObject();
json.prop("uuid", project.uuid());
writeIfWished(json, "key", project.key(), fieldsToReturn);
writeIfWished(json, "name", project.name(), fieldsToReturn);
writeIfWished(json, "creationDate", project.getCreatedAt(), fieldsToReturn);
json.endObject();
}
json.endArray();
}

private void writeIfWished(JsonWriter json, String key, String value, Set<String> fieldsToReturn) {
if (fieldsToReturn.contains(key)) {
json.prop(key, value);
}
}

private void writeIfWished(JsonWriter json, String key, Date value, Set<String> desiredFields) {
if (desiredFields.contains(key)) {
json.propDateTime(key, value);
}
}

private Set<String> fieldsToReturn(@Nullable List<String> desiredFieldsFromRequest) {
if (desiredFieldsFromRequest == null) {
return POSSIBLE_FIELDS;
}

return newHashSet(desiredFieldsFromRequest);
}
}

server/sonar-server/src/main/java/org/sonar/server/component/ws/ProvisionedProjectsAction.java → server/sonar-server/src/main/java/org/sonar/server/component/ws/ProjectsProvisionedAction.java View File

@@ -34,24 +34,26 @@ import org.sonar.server.db.DbClient;
import org.sonar.server.es.SearchOptions;
import org.sonar.server.user.UserSession;

import java.util.Arrays;
import java.util.Date;
import java.util.List;
import java.util.Set;

public class ProvisionedProjectsAction implements ProjectsWsAction {
private static final List<String> POSSIBLE_FIELDS = Arrays.asList("uuid", "key", "name", "creationDate");
import static com.google.common.collect.Sets.newHashSet;

public class ProjectsProvisionedAction implements ProjectsWsAction {
private static final Set<String> POSSIBLE_FIELDS = newHashSet("uuid", "key", "name", "creationDate");

private final DbClient dbClient;
private final UserSession userSession;

public ProvisionedProjectsAction(DbClient dbClient, UserSession userSession) {
public ProjectsProvisionedAction(DbClient dbClient, UserSession userSession) {
this.dbClient = dbClient;
this.userSession = userSession;
}

@Override
public void define(WebService.NewController controller) {
WebService.NewAction action = controller
controller
.createAction("provisioned")
.setDescription(
"Get the list of provisioned projects.<br /> " +
@@ -60,12 +62,8 @@ public class ProvisionedProjectsAction implements ProjectsWsAction {
.setResponseExample(Resources.getResource(getClass(), "projects-example-provisioned.json"))
.setHandler(this)
.addPagingParams(100)
.addSearchQuery("sonar", "names", "keys")
.addFieldsParam(POSSIBLE_FIELDS);

action
.createParam(Param.TEXT_QUERY)
.setDescription("UTF-8 search query")
.setExampleValue("sonar");
}

@Override
@@ -74,8 +72,8 @@ public class ProvisionedProjectsAction implements ProjectsWsAction {
SearchOptions options = new SearchOptions().setPage(
request.mandatoryParamAsInt(Param.PAGE),
request.mandatoryParamAsInt(Param.PAGE_SIZE)
);
List<String> desiredFields = desiredFields(request);
);
Set<String> desiredFields = desiredFields(request);
String query = request.param(Param.TEXT_QUERY);

DbSession dbSession = dbClient.openSession(false);
@@ -91,7 +89,7 @@ public class ProvisionedProjectsAction implements ProjectsWsAction {
}
}

private void writeProjects(List<ComponentDto> projects, JsonWriter json, List<String> desiredFields) {
private void writeProjects(List<ComponentDto> projects, JsonWriter json, Set<String> desiredFields) {
json.name("projects");
json.beginArray();
for (ComponentDto project : projects) {
@@ -105,24 +103,24 @@ public class ProvisionedProjectsAction implements ProjectsWsAction {
json.endArray();
}

private void writeIfNeeded(JsonWriter json, String fieldName, String value, List<String> desiredFields) {
private void writeIfNeeded(JsonWriter json, String fieldName, String value, Set<String> desiredFields) {
if (desiredFields.contains(fieldName)) {
json.prop(fieldName, value);
}
}

private void writeIfNeeded(JsonWriter json, String fieldName, Date date, List<String> desiredFields) {
private void writeIfNeeded(JsonWriter json, String fieldName, Date date, Set<String> desiredFields) {
if (desiredFields.contains(fieldName)) {
json.propDateTime(fieldName, date);
}
}

private List<String> desiredFields(Request request) {
private Set<String> desiredFields(Request request) {
List<String> desiredFields = request.paramAsStrings(Param.FIELDS);
if (desiredFields == null) {
desiredFields = POSSIBLE_FIELDS;
return POSSIBLE_FIELDS;
}

return desiredFields;
return newHashSet(desiredFields);
}
}

+ 3
- 1
server/sonar-server/src/main/java/org/sonar/server/component/ws/ProjectsWs.java View File

@@ -25,6 +25,8 @@ import org.sonar.api.server.ws.RailsHandler;
import org.sonar.api.server.ws.WebService;

public class ProjectsWs implements WebService {
private static final String ENDPOINT = "api/projects";

private final ProjectsWsAction[] actions;

public ProjectsWs(ProjectsWsAction... actions) {
@@ -33,7 +35,7 @@ public class ProjectsWs implements WebService {

@Override
public void define(Context context) {
NewController controller = context.createController("api/projects")
NewController controller = context.createController(ENDPOINT)
.setSince("2.10")
.setDescription("Projects management");


+ 4
- 2
server/sonar-server/src/main/java/org/sonar/server/platform/ServerComponents.java View File

@@ -109,8 +109,9 @@ import org.sonar.server.component.db.SnapshotDao;
import org.sonar.server.component.ws.ComponentAppAction;
import org.sonar.server.component.ws.ComponentsWs;
import org.sonar.server.component.ws.EventsWs;
import org.sonar.server.component.ws.ProjectsGhostsAction;
import org.sonar.server.component.ws.ProjectsWs;
import org.sonar.server.component.ws.ProvisionedProjectsAction;
import org.sonar.server.component.ws.ProjectsProvisionedAction;
import org.sonar.server.component.ws.ResourcesWs;
import org.sonar.server.computation.ComputationThreadLauncher;
import org.sonar.server.computation.ReportQueue;
@@ -801,7 +802,8 @@ class ServerComponents {
pico.addSingleton(org.sonar.server.component.ws.SearchAction.class);
pico.addSingleton(EventsWs.class);
pico.addSingleton(ComponentCleanerService.class);
pico.addSingleton(ProvisionedProjectsAction.class);
pico.addSingleton(ProjectsProvisionedAction.class);
pico.addSingleton(ProjectsGhostsAction.class);

// views
pico.addSingleton(ViewIndexDefinition.class);

+ 19
- 0
server/sonar-server/src/main/resources/org/sonar/server/component/ws/projects-example-ghosts.json View File

@@ -0,0 +1,19 @@
{
"projects": [
{
"uuid": "ce4c03d6-430f-40a9-b777-ad877c00aa4d",
"key": "org.apache.hbas:hbase",
"name": "HBase",
"creationDate": "2015-03-04T23:03:44+0100"
},
{
"uuid": "c526ef20-131b-4486-9357-063fa64b5079",
"key": "com.microsoft.roslyn:roslyn",
"name": "Roslyn",
"creationDate": "2013-03-04T23:03:44+0100"
}
],
"total": 2,
"p": 1,
"ps": 100
}

+ 11
- 0
server/sonar-server/src/test/java/org/sonar/server/component/db/ComponentDaoTest.java View File

@@ -575,4 +575,15 @@ public class ComponentDaoTest {

assertThat(numberOfProjects).isEqualTo(1);
}

@Test
public void select_ghost_projects() throws Exception {
db.prepareDbUnit(getClass(), "select_ghost_projects.xml");

List<ComponentDto> result = sut.selectGhostProjects(session, null, new SearchOptions());

assertThat(result).hasSize(1);
assertThat(result.get(0).key()).isEqualTo("org.ghost.project");
assertThat(sut.countGhostProjects(session, null)).isEqualTo(1);
}
}

+ 199
- 0
server/sonar-server/src/test/java/org/sonar/server/component/ws/ProjectsGhostsActionTest.java View File

@@ -0,0 +1,199 @@
/*
* SonarQube, open source software quality management tool.
* Copyright (C) 2008-2014 SonarSource
* mailto:contact AT sonarsource DOT com
*
* SonarQube 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.
*
* SonarQube 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.sonar.server.component.ws;

import com.google.common.io.Resources;
import org.apache.commons.lang.StringUtils;
import org.junit.After;
import org.junit.Before;
import org.junit.ClassRule;
import org.junit.Rule;
import org.junit.Test;
import org.sonar.api.server.ws.WebService.Param;
import org.sonar.api.utils.DateUtils;
import org.sonar.api.utils.System2;
import org.sonar.api.web.UserRole;
import org.sonar.core.component.ComponentDto;
import org.sonar.core.component.SnapshotDto;
import org.sonar.core.persistence.DbSession;
import org.sonar.core.persistence.DbTester;
import org.sonar.server.component.ComponentTesting;
import org.sonar.server.component.SnapshotTesting;
import org.sonar.server.component.db.ComponentDao;
import org.sonar.server.component.db.SnapshotDao;
import org.sonar.server.db.DbClient;
import org.sonar.server.exceptions.ForbiddenException;
import org.sonar.server.tester.UserSessionRule;
import org.sonar.server.ws.WsTester;
import org.sonar.test.JsonAssert;

import static org.assertj.core.api.Assertions.assertThat;

public class ProjectsGhostsActionTest {

@ClassRule
public static DbTester db = new DbTester();
@Rule
public UserSessionRule userSessionRule = UserSessionRule.standalone();
WsTester ws;

DbClient dbClient;
DbSession dbSession;

@Before
public void setUp() {
dbClient = new DbClient(db.database(), db.myBatis(), new ComponentDao(), new SnapshotDao(System2.INSTANCE));
dbSession = dbClient.openSession(false);
ws = new WsTester(new ProjectsWs(new ProjectsGhostsAction(dbClient, userSessionRule)));
db.truncateTables();
}

@After
public void tearDown() throws Exception {
dbSession.close();
}

@Test
public void ghost_projects_without_analyzed_projects() throws Exception {
userSessionRule.setGlobalPermissions(UserRole.ADMIN);
insertNewGhostProject("1");
insertNewGhostProject("2");
insertNewActiveProject("3");

WsTester.Result result = ws.newGetRequest("api/projects", "ghosts").execute();

result.assertJson(getClass(), "all-projects.json");
assertThat(result.outputAsString()).doesNotContain("analyzed-uuid-3");
}

@Test
public void ghost_projects_with_correct_pagination() throws Exception {
userSessionRule.setGlobalPermissions(UserRole.ADMIN);
for (int i = 1; i <= 10; i++) {
insertNewGhostProject(String.valueOf(i));
}

WsTester.Result result = ws.newGetRequest("api/projects", "ghosts")
.setParam(Param.PAGE, "3")
.setParam(Param.PAGE_SIZE, "4")
.execute();

result.assertJson(getClass(), "pagination.json");
assertThat(StringUtils.countMatches(result.outputAsString(), "ghost-uuid-")).isEqualTo(2);
}

@Test
public void ghost_projects_with_chosen_fields() throws Exception {
userSessionRule.setGlobalPermissions(UserRole.ADMIN);
insertNewGhostProject("1");

WsTester.Result result = ws.newGetRequest("api/projects", "ghosts")
.setParam(Param.FIELDS, "name")
.execute();

assertThat(result.outputAsString()).contains("uuid", "name")
.doesNotContain("key")
.doesNotContain("creationDate");
}

@Test
public void ghost_projects_with_partial_query_on_name() throws Exception {
userSessionRule.setGlobalPermissions(UserRole.ADMIN);

insertNewGhostProject("10");
insertNewGhostProject("11");
insertNewGhostProject("2");

WsTester.Result result = ws.newGetRequest("api/projects", "ghosts")
.setParam(Param.TEXT_QUERY, "name-1")
.execute();

assertThat(result.outputAsString()).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);

insertNewGhostProject("1");

WsTester.Result result = ws.newGetRequest("api/projects", "ghosts")
.setParam(Param.TEXT_QUERY, "GHOST-key")
.execute();

assertThat(result.outputAsString()).contains("ghost-key-1");
}

@Test
public void ghost_projects_base_on_json_example() throws Exception {
userSessionRule.setGlobalPermissions(UserRole.ADMIN);
ComponentDto hBaseProject = ComponentTesting.newProjectDto("ce4c03d6-430f-40a9-b777-ad877c00aa4d")
.setKey("org.apache.hbas:hbase")
.setName("HBase")
.setCreatedAt(DateUtils.parseDateTime("2015-03-04T23:03:44+0100"));
hBaseProject = dbClient.componentDao().insert(dbSession, hBaseProject);
dbClient.snapshotDao().insert(dbSession, SnapshotTesting.createForProject(hBaseProject)
.setStatus(SnapshotDto.STATUS_UNPROCESSED));
ComponentDto roslynProject = ComponentTesting.newProjectDto("c526ef20-131b-4486-9357-063fa64b5079")
.setKey("com.microsoft.roslyn:roslyn")
.setName("Roslyn")
.setCreatedAt(DateUtils.parseDateTime("2013-03-04T23:03:44+0100"));
roslynProject = dbClient.componentDao().insert(dbSession, roslynProject);
dbClient.snapshotDao().insert(dbSession, SnapshotTesting.createForProject(roslynProject)
.setStatus(SnapshotDto.STATUS_UNPROCESSED));
dbSession.commit();

WsTester.Result result = ws.newGetRequest("api/projects", "ghosts").execute();

JsonAssert.assertJson(result.outputAsString()).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);

ws.newGetRequest("api/projects", "ghosts").execute();
}

private void insertNewGhostProject(String id) {
ComponentDto project = ComponentTesting
.newProjectDto("ghost-uuid-" + id)
.setName("ghost-name-" + id)
.setKey("ghost-key-" + id);
project = dbClient.componentDao().insert(dbSession, project);
SnapshotDto snapshot = SnapshotTesting.createForProject(project)
.setStatus(SnapshotDto.STATUS_UNPROCESSED);
dbClient.snapshotDao().insert(dbSession, snapshot);
dbSession.commit();
}

private void insertNewActiveProject(String id) {
ComponentDto project = ComponentTesting
.newProjectDto("analyzed-uuid-" + id)
.setName("analyzed-name-" + id)
.setKey("analyzed-key-" + id);
project = dbClient.componentDao().insert(dbSession, project);
SnapshotDto snapshot = SnapshotTesting.createForProject(project);
dbClient.snapshotDao().insert(dbSession, snapshot);
dbSession.commit();
}
}

server/sonar-server/src/test/java/org/sonar/server/component/ws/ProvisionedProjectsActionTest.java → server/sonar-server/src/test/java/org/sonar/server/component/ws/ProjectsProvisionedActionTest.java View File

@@ -47,7 +47,7 @@ import org.sonar.test.JsonAssert;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.mock;

public class ProvisionedProjectsActionTest {
public class ProjectsProvisionedActionTest {

@ClassRule
public static DbTester db = new DbTester();
@@ -71,7 +71,7 @@ public class ProvisionedProjectsActionTest {
dbSession = dbClient.openSession(false);
componentDao = dbClient.componentDao();
db.truncateTables();
ws = new WsTester(new ProjectsWs(new ProvisionedProjectsAction(dbClient, userSessionRule)));
ws = new WsTester(new ProjectsWs(new ProjectsProvisionedAction(dbClient, userSessionRule)));
}

@Test
@@ -79,18 +79,19 @@ public class ProvisionedProjectsActionTest {
userSessionRule.setGlobalPermissions(UserRole.ADMIN);
ComponentDto analyzedProject = ComponentTesting.newProjectDto("analyzed-uuid-1");
componentDao.insert(dbSession, newProvisionedProject("1"), newProvisionedProject("2"));
analyzedProject = componentDao.insert(dbSession, analyzedProject);
analyzedProject = dbClient.componentDao().insert(dbSession, analyzedProject);
SnapshotDto snapshot = SnapshotTesting.createForProject(analyzedProject);
dbClient.snapshotDao().insert(dbSession, snapshot);
dbSession.commit();

WsTester.TestRequest request = ws.newGetRequest("api/projects", "provisioned");
WsTester.Result result = ws.newGetRequest("api/projects", "provisioned").execute();

request.execute().assertJson(getClass(), "all-projects.json");
result.assertJson(getClass(), "all-projects.json");
assertThat(result.outputAsString()).doesNotContain("analyzed-uuid-1");
}

@Test
public void provisioned_projects_with_correct_paginated() throws Exception {
public void provisioned_projects_with_correct_pagination() throws Exception {
userSessionRule.setGlobalPermissions(UserRole.ADMIN);
for (int i = 1; i <= 10; i++) {
componentDao.insert(dbSession, newProvisionedProject(String.valueOf(i)));

+ 3
- 2
server/sonar-server/src/test/java/org/sonar/server/component/ws/ProjectsWsTest.java View File

@@ -31,11 +31,12 @@ import static org.assertj.core.api.Assertions.assertThat;
public class ProjectsWsTest {

WebService.Controller controller;
WsTester ws;

@Before
public void setUp() {
WsTester tester = new WsTester(new ProjectsWs());
controller = tester.controller("api/projects");
ws = new WsTester(new ProjectsWs());
controller = ws.controller("api/projects");
}

@Test

+ 109
- 0
server/sonar-server/src/test/resources/org/sonar/server/component/db/ComponentDaoTest/select_ghost_projects.xml View File

@@ -0,0 +1,109 @@
<dataset>

<!-- Struts projects is authorized for all user -->
<group_roles id="1" group_id="[null]" resource_id="1" role="user"/>

<!-- Ghost project -->
<projects id="42" root_id="[null]" scope="PRJ" qualifier="TRK" kee="org.ghost.project" name="Ghost Project"
uuid="PPAA" project_uuid="PPAA" module_uuid="[null]" module_uuid_path="."
description="the description" long_name="Ghost Project"
enabled="[true]" language="[null]" copy_resource_id="[null]" person_id="[null]" path="[null]" authorization_updated_at="123456789" />

<!-- root project -->
<projects id="1" root_id="[null]" scope="PRJ" qualifier="TRK" kee="org.struts:struts" deprecated_kee="org.struts:struts" name="Struts"
uuid="ABCD" project_uuid="ABCD" module_uuid="[null]" module_uuid_path=".ABCD."
description="the description" long_name="Apache Struts"
enabled="[true]" language="[null]" copy_resource_id="[null]" person_id="[null]" path="[null]" authorization_updated_at="123456789" />
<snapshots id="1" project_id="1" parent_snapshot_id="[null]" root_project_id="1" root_snapshot_id="[null]"
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]"
depth="[null]" scope="PRJ" qualifier="TRK" created_at="1228222680000" build_date="1228222680000"
version="[null]" path=""/>
<snapshots id="10" project_id="1" parent_snapshot_id="[null]" root_project_id="1" root_snapshot_id="[null]"
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]"
depth="[null]" scope="PRJ" qualifier="TRK" created_at="1228136280000" build_date="1228136280000"
version="[null]" path=""/>
<snapshots id="11" project_id="42" parent_snapshot_id="[null]" root_project_id="42" root_snapshot_id="[null]"
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]"
depth="[null]" scope="PRJ" qualifier="TRK" created_at="1228136280000" build_date="1228136280000"
version="[null]" path=""/>

<!-- module -->
<projects id="2" root_id="1" kee="org.struts:struts-core" name="Struts Core"
uuid="EFGH" 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_resource_id="[null]" person_id="[null]" authorization_updated_at="[null]" />
<snapshots id="2" project_id="2" parent_snapshot_id="1" root_project_id="1" root_snapshot_id="1"
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]"
depth="[null]" scope="PRJ" qualifier="BRC" created_at="1228222680000" build_date="1228222680000"
version="[null]" path="1."/>

<!-- directory -->
<projects long_name="org.struts" id="3" scope="DIR" qualifier="DIR" kee="org.struts:struts-core:src/org/struts"
uuid="GHIJ" project_uuid="ABCD" module_uuid="EFGH" module_uuid_path=".ABCD.EFGH."
name="src/org/struts" root_id="2"
description="[null]"
enabled="[true]" language="[null]" copy_resource_id="[null]" person_id="[null]" path="src/org/struts" authorization_updated_at="[null]" />
<snapshots id="3" project_id="3" parent_snapshot_id="2" root_project_id="1" root_snapshot_id="1"
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]"
depth="[null]" scope="DIR" qualifier="PAC" created_at="1228222680000" build_date="1228222680000"
version="[null]" path="1.2."/>

<!-- file -->
<projects long_name="org.struts.RequestContext" id="4" scope="FIL" qualifier="FIL" kee="org.struts:struts-core:src/org/struts/RequestContext.java"
uuid="KLMN" project_uuid="ABCD" module_uuid="EFGH" module_uuid_path=".ABCD.EFGH."
name="RequestContext.java" root_id="2"
description="[null]"
enabled="[true]" language="java" copy_resource_id="[null]" person_id="[null]" path="src/org/struts/RequestContext.java" authorization_updated_at="[null]" />

<snapshots id="4" project_id="4" parent_snapshot_id="3" root_project_id="1" root_snapshot_id="1"
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]"
depth="[null]" scope="FIL" qualifier="CLA" created_at="1228222680000" build_date="1228222680000"
version="[null]" path="1.2.3."/>

<!-- Disabled projects -->
<projects id="10" root_id="[null]" scope="PRJ" qualifier="TRK" kee="org.disabled.project" name="Disabled Project"
uuid="DCBA" project_uuid="DCBA" module_uuid="[null]" module_uuid_path="."
description="the description" long_name="Disabled project"
enabled="[false]" language="[null]" copy_resource_id="[null]" person_id="[null]" path="[null]" authorization_updated_at="123456789" />

<!-- Developer and technical project copy -->
<projects id="11" root_id="[null]" scope="PRJ" qualifier="DEV" kee="DEV:anakin@skywalker.name" name="Anakin Skywalker"
uuid="OPQR" project_uuid="OPQR" module_uuid="[null]" module_uuid_path=".OPQR."
description="the description" long_name="Anakin Skywalker"
enabled="[true]" language="[null]" copy_resource_id="[null]" person_id="[null]" path="[null]" authorization_updated_at="123456789" />
<projects id="12" root_id="11" scope="PRJ" qualifier="DEV_PRJ" kee="DEV:anakin@skywalker.name:org.struts:struts" name="Apache Struts"
uuid="STUV" project_uuid="OPQR" module_uuid="OPQR" module_uuid_path=".OPQR."
description="the description" long_name="Apache Struts"
enabled="[true]" language="[null]" copy_resource_id="1" person_id="11" path="[null]" authorization_updated_at="123456789" />

</dataset>

+ 14
- 0
server/sonar-server/src/test/resources/org/sonar/server/component/ws/ProjectsGhostsActionTest/all-projects.json View File

@@ -0,0 +1,14 @@
{
"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"
}
]
}

+ 5
- 0
server/sonar-server/src/test/resources/org/sonar/server/component/ws/ProjectsGhostsActionTest/pagination.json View File

@@ -0,0 +1,5 @@
{
"p": 3,
"ps": 4,
"total": 10
}

server/sonar-server/src/test/resources/org/sonar/server/component/ws/ProvisionedProjectsActionTest/all-projects.json → server/sonar-server/src/test/resources/org/sonar/server/component/ws/ProjectsProvisionedActionTest/all-projects.json View File


+ 4
- 0
sonar-core/src/main/java/org/sonar/core/component/db/ComponentMapper.java View File

@@ -107,4 +107,8 @@ public interface ComponentMapper {
List<ComponentDto> selectProvisionedProjects(Map<String, String> parameters, RowBounds rowBounds);

int countProvisionedProjects(Map<String, String> parameters);

List<ComponentDto> selectGhostProjects(Map<String, String> parameters, RowBounds rowBounds);

long countGhostProjects(Map<String, String> parameters);
}

+ 29
- 2
sonar-core/src/main/resources/org/sonar/core/component/db/ComponentMapper.xml View File

@@ -248,9 +248,36 @@
and p.copy_resource_id is null
<if test="query!=null">
and (
p.name like #{query}
or p.kee like #{query}
UPPER(p.name) like #{query}
or UPPER(p.kee) like #{query}
)
</if>
</sql>

<select id="selectGhostProjects" parameterType="map" resultType="Component">
select distinct <include refid="componentColumns"/>
from projects p
<include refid="ghostClauses"/>
</select>

<select id="countGhostProjects" parameterType="map" resultType="long">
select count(p.id)
from projects p
<include refid="ghostClauses"/>
</select>

<sql id="ghostClauses">
inner join snapshots s1 on s1.project_id = p.id and s1.status='U'
left join snapshots s2 on s2.project_id = p.id and s2.status='P'
where
s2.id is null
and p.qualifier=#{qualifier}
and p.copy_resource_id is null
<if test="query!=null">
and (
UPPER(p.name) like #{query}
or UPPER(p.kee) like #{query}
)
</if>
</sql>
</mapper>

+ 15
- 0
sonar-plugin-api/src/main/java/org/sonar/api/server/ws/WebService.java View File

@@ -19,6 +19,7 @@
*/
package org.sonar.api.server.ws;

import com.google.common.base.Joiner;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Maps;
@@ -356,6 +357,20 @@ public interface WebService extends Definable<WebService.Context> {
return this;
}

/**$
*
* Creates the parameter {@link org.sonar.api.server.ws.WebService.Param#TEXT_QUERY}, which is
* used to search for a subset of fields containing the supplied string.<br />
* The fields must be in the <strong>plural</strong> form (ex: "names", "keys")
*/
public NewAction addSearchQuery(String exampleValue, String... pluralFields) {
String actionDescription = String.format("Searches for %s containing the supplied string.", Joiner.on(" and ").join(pluralFields));
createParam(Param.TEXT_QUERY)
.setDescription(actionDescription)
.setExampleValue(exampleValue);
return this;
}

/**
* Add predefined parameters related to sorting of results.
*/

Loading…
Cancel
Save