aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJean-Baptiste Lievremont <jean-baptiste.lievremont@sonarsource.com>2015-03-31 16:55:55 +0200
committerJean-Baptiste Lievremont <jean-baptiste.lievremont@sonarsource.com>2015-04-03 11:55:08 +0200
commit7bb307b1ca7d20809b38cd9556b6b34300b9113b (patch)
treee04ee07a6f32e560b3e9c99aa72adfbfa1b5a45c
parent83da0fcf581c31768ba322246da4079d23fa20bd (diff)
downloadsonarqube-7bb307b1ca7d20809b38cd9556b6b34300b9113b.tar.gz
sonarqube-7bb307b1ca7d20809b38cd9556b6b34300b9113b.zip
SONAR-5921 WS to list project/profile associations
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/platform/ServerComponents.java1
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/qualityprofile/ws/QProfileProjectsAction.java174
-rw-r--r--server/sonar-server/src/test/java/org/sonar/server/qualityprofile/ws/QProfileProjectsActionTest.java224
-rw-r--r--server/sonar-server/src/test/resources/org/sonar/server/qualityprofile/ws/QProfileProjectsActionTest/all.json27
-rw-r--r--server/sonar-server/src/test/resources/org/sonar/server/qualityprofile/ws/QProfileProjectsActionTest/all_filtered.json17
-rw-r--r--server/sonar-server/src/test/resources/org/sonar/server/qualityprofile/ws/QProfileProjectsActionTest/authorized_selected.json12
-rw-r--r--server/sonar-server/src/test/resources/org/sonar/server/qualityprofile/ws/QProfileProjectsActionTest/deselected.json17
-rw-r--r--server/sonar-server/src/test/resources/org/sonar/server/qualityprofile/ws/QProfileProjectsActionTest/empty.json6
-rw-r--r--server/sonar-server/src/test/resources/org/sonar/server/qualityprofile/ws/QProfileProjectsActionTest/selected_page1.json17
-rw-r--r--server/sonar-server/src/test/resources/org/sonar/server/qualityprofile/ws/QProfileProjectsActionTest/selected_page2.json17
-rw-r--r--sonar-core/src/main/java/org/sonar/core/qualityprofile/db/ProjectQprofileAssociationDto.java51
-rw-r--r--sonar-core/src/main/java/org/sonar/core/qualityprofile/db/QualityProfileDao.java20
-rw-r--r--sonar-core/src/main/java/org/sonar/core/qualityprofile/db/QualityProfileMapper.java6
-rw-r--r--sonar-core/src/main/resources/org/sonar/core/qualityprofile/db/QualityProfileMapper.xml39
14 files changed, 627 insertions, 1 deletions
diff --git a/server/sonar-server/src/main/java/org/sonar/server/platform/ServerComponents.java b/server/sonar-server/src/main/java/org/sonar/server/platform/ServerComponents.java
index eef1ee23140..38d5d1469fd 100644
--- a/server/sonar-server/src/main/java/org/sonar/server/platform/ServerComponents.java
+++ b/server/sonar-server/src/main/java/org/sonar/server/platform/ServerComponents.java
@@ -381,6 +381,7 @@ class ServerComponents {
pico.addSingleton(QProfileRestoreBuiltInAction.class);
pico.addSingleton(QProfileSearchAction.class);
pico.addSingleton(QProfileSetDefaultAction.class);
+ pico.addSingleton(QProfileProjectsAction.class);
pico.addSingleton(QProfilesWs.class);
pico.addSingleton(ProfilesWs.class);
pico.addSingleton(RuleActivationActions.class);
diff --git a/server/sonar-server/src/main/java/org/sonar/server/qualityprofile/ws/QProfileProjectsAction.java b/server/sonar-server/src/main/java/org/sonar/server/qualityprofile/ws/QProfileProjectsAction.java
new file mode 100644
index 00000000000..d351b0fde1f
--- /dev/null
+++ b/server/sonar-server/src/main/java/org/sonar/server/qualityprofile/ws/QProfileProjectsAction.java
@@ -0,0 +1,174 @@
+/*
+ * 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.qualityprofile.ws;
+
+import com.google.common.base.Predicate;
+import com.google.common.collect.Collections2;
+import com.google.common.collect.Iterables;
+import com.google.common.collect.Lists;
+import org.apache.commons.lang.builder.CompareToBuilder;
+import org.sonar.api.server.ws.Request;
+import org.sonar.api.server.ws.Response;
+import org.sonar.api.server.ws.WebService.NewAction;
+import org.sonar.api.server.ws.WebService.NewController;
+import org.sonar.api.utils.Paging;
+import org.sonar.api.utils.text.JsonWriter;
+import org.sonar.api.web.UserRole;
+import org.sonar.core.persistence.DbSession;
+import org.sonar.core.qualityprofile.db.ProjectQprofileAssociationDto;
+import org.sonar.core.util.NonNullInputFunction;
+import org.sonar.server.db.DbClient;
+import org.sonar.server.exceptions.NotFoundException;
+import org.sonar.server.user.UserSession;
+
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.List;
+
+public class QProfileProjectsAction implements BaseQProfileWsAction {
+
+ private static final String PARAM_KEY = "key";
+ private static final String PARAM_SELECTED = "selected";
+ private static final String PARAM_QUERY = "query";
+ private static final String PARAM_PAGE_SIZE = "pageSize";
+ private static final String PARAM_PAGE = "page";
+
+ private static final String SELECTION_ALL = "all";
+ private static final String SELECTION_SELECTED = "selected";
+ private static final String SELECTION_DESELECTED = "deselected";
+
+ private final DbClient dbClient;
+
+ public QProfileProjectsAction(DbClient dbClient) {
+ this.dbClient = dbClient;
+ }
+
+ @Override
+ public void define(NewController controller) {
+ NewAction projects = controller.createAction("projects")
+ .setSince("5.2")
+ .setHandler(this)
+ .setDescription("List projects with their association status regarding a quality profile.");
+ projects.createParam(PARAM_KEY)
+ .setDescription("A quality profile key.")
+ .setRequired(true)
+ .setExampleValue("sonar-way-java-12345");
+ projects.createParam(PARAM_SELECTED)
+ .setDescription("If specified, return only selected or deselected projects.")
+ .setPossibleValues(SELECTION_SELECTED, SELECTION_DESELECTED, SELECTION_ALL)
+ .setDefaultValue(SELECTION_ALL);
+ projects.createParam(PARAM_QUERY)
+ .setDescription("If specified, return only projects whose name match the query.");
+ projects.createParam(PARAM_PAGE_SIZE)
+ .setDescription("Size for the paging to apply").setDefaultValue(100);
+ projects.createParam(PARAM_PAGE)
+ .setDescription("Index of the page to display").setDefaultValue(1);
+ }
+
+ @Override
+ public void handle(Request request, Response response) throws Exception {
+ String profileKey = request.mandatoryParam(PARAM_KEY);
+
+ DbSession session = dbClient.openSession(false);
+
+ try {
+ checkProfileExists(profileKey, session);
+ String selected = request.param(PARAM_SELECTED);
+ String query = request.param(PARAM_QUERY);
+ int pageSize = request.mandatoryParamAsInt(PARAM_PAGE_SIZE);
+ int page = request.mandatoryParamAsInt(PARAM_PAGE);
+
+ List<ProjectQprofileAssociationDto> projects = loadProjects(profileKey, session, selected, query);
+ Collections.sort(projects, new Comparator<ProjectQprofileAssociationDto>() {
+ @Override
+ public int compare(ProjectQprofileAssociationDto o1, ProjectQprofileAssociationDto o2) {
+ return new CompareToBuilder()
+ // First, sort by name
+ .append(o1.getProjectName(), o2.getProjectName())
+ // Then by UUID to disambiguate
+ .append(o1.getProjectUuid(), o2.getProjectUuid())
+ .toComparison();
+ }
+ });
+
+ Collection<Long> projectIds = Collections2.transform(projects, new NonNullInputFunction<ProjectQprofileAssociationDto, Long>() {
+ @Override
+ protected Long doApply(ProjectQprofileAssociationDto input) {
+ return input.getProjectId();
+ }
+ });
+
+ final Collection<Long> authorizedProjectIds = dbClient.authorizationDao().keepAuthorizedProjectIds(session, projectIds, UserSession.get().userId(), UserRole.USER);
+ Iterable<ProjectQprofileAssociationDto> authorizedProjects = Iterables.filter(projects, new Predicate<ProjectQprofileAssociationDto>() {
+ @Override
+ public boolean apply(ProjectQprofileAssociationDto input) {
+ return authorizedProjectIds.contains(input.getProjectId());
+ }
+ });
+
+ Paging paging = Paging.create(pageSize, page, authorizedProjectIds.size());
+
+ List<ProjectQprofileAssociationDto> pagedAuthorizedProjects = Lists.newArrayList(authorizedProjects);
+ if (pagedAuthorizedProjects.size() <= paging.offset()) {
+ pagedAuthorizedProjects = Lists.newArrayList();
+ } else if (pagedAuthorizedProjects.size() > paging.pageSize()) {
+ pagedAuthorizedProjects = pagedAuthorizedProjects.subList(paging.offset(), paging.offset() + pageSize);
+ }
+
+ writeProjects(response.newJsonWriter(), pagedAuthorizedProjects, paging);
+ } finally {
+ session.close();
+ }
+ }
+
+ private void checkProfileExists(String profileKey, DbSession session) {
+ if (dbClient.qualityProfileDao().getByKey(session, profileKey) == null) {
+ throw new NotFoundException(String.format("Could not find a quality profile with key '%s'", profileKey));
+ }
+ }
+
+ private List<ProjectQprofileAssociationDto> loadProjects(String profileKey, DbSession session, String selected, String query) {
+ List<ProjectQprofileAssociationDto> projects = Lists.newArrayList();
+ if (SELECTION_SELECTED.equals(selected)) {
+ projects.addAll(dbClient.qualityProfileDao().selectSelectedProjects(profileKey, query, session));
+ } else if (SELECTION_DESELECTED.equals(selected)) {
+ projects.addAll(dbClient.qualityProfileDao().selectDeselectedProjects(profileKey, query, session));
+ } else {
+ projects.addAll(dbClient.qualityProfileDao().selectProjectAssociations(profileKey, query, session));
+ }
+ return projects;
+ }
+
+ private void writeProjects(JsonWriter json, List<ProjectQprofileAssociationDto> projects, Paging paging) {
+ json.beginObject();
+ json.name("results").beginArray();
+ for (ProjectQprofileAssociationDto project : projects) {
+ json.beginObject()
+ .prop("key", project.getProjectUuid())
+ .prop("name", project.getProjectName())
+ .prop("selected", project.isAssociated())
+ .endObject();
+ }
+ json.endArray();
+ json.prop("more", paging.hasNextPage());
+ json.endObject().close();
+ }
+}
diff --git a/server/sonar-server/src/test/java/org/sonar/server/qualityprofile/ws/QProfileProjectsActionTest.java b/server/sonar-server/src/test/java/org/sonar/server/qualityprofile/ws/QProfileProjectsActionTest.java
new file mode 100644
index 00000000000..9b73c6a3957
--- /dev/null
+++ b/server/sonar-server/src/test/java/org/sonar/server/qualityprofile/ws/QProfileProjectsActionTest.java
@@ -0,0 +1,224 @@
+/*
+ * 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.qualityprofile.ws;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.ClassRule;
+import org.junit.Test;
+import org.junit.experimental.categories.Category;
+import org.sonar.api.utils.System2;
+import org.sonar.api.web.UserRole;
+import org.sonar.core.component.ComponentDto;
+import org.sonar.core.persistence.DbSession;
+import org.sonar.core.persistence.DbTester;
+import org.sonar.core.qualityprofile.db.QualityProfileDao;
+import org.sonar.core.qualityprofile.db.QualityProfileDto;
+import org.sonar.core.user.*;
+import org.sonar.server.component.ComponentTesting;
+import org.sonar.server.component.db.ComponentDao;
+import org.sonar.server.db.DbClient;
+import org.sonar.server.exceptions.NotFoundException;
+import org.sonar.server.qualityprofile.QProfileTesting;
+import org.sonar.server.user.MockUserSession;
+import org.sonar.server.user.db.UserDao;
+import org.sonar.server.ws.WsTester;
+import org.sonar.server.ws.WsTester.TestRequest;
+import org.sonar.test.DbTests;
+
+import static org.mockito.Mockito.mock;
+
+@Category(DbTests.class)
+public class QProfileProjectsActionTest {
+
+ @ClassRule
+ public static final DbTester dbTester = new DbTester();
+
+ private WsTester wsTester;
+
+ private DbClient dbClient;
+
+ private DbSession session;
+
+ private QualityProfileDto xooP1, xooP2;
+
+ private ComponentDto project1, project2, project3, project4;
+
+ private Long userId = 42L;
+
+ private RoleDao roleDao;
+
+ @Before
+ public void setUp() throws Exception {
+ dbTester.truncateTables();
+ dbClient = new DbClient(dbTester.database(), dbTester.myBatis(),
+ new QualityProfileDao(dbTester.myBatis(), mock(System2.class)),
+ new ComponentDao(),
+ new AuthorizationDao(dbTester.myBatis()));
+ roleDao = new RoleDao();
+ session = dbClient.openSession(false);
+
+ wsTester = new WsTester(new QProfilesWs(
+ mock(RuleActivationActions.class),
+ mock(BulkRuleActivationActions.class),
+ mock(ProjectAssociationActions.class),
+ new QProfileProjectsAction(dbClient)));
+
+ MockUserSession.set().setLogin("obiwan").setUserId(userId.intValue());
+ new UserDao(dbTester.myBatis(), mock(System2.class))
+ .insert(session, new UserDto()
+ .setActive(true)
+ .setId(userId)
+ .setLogin("obiwan"));
+
+ createProfiles();
+
+ session.commit();
+ }
+
+ @After
+ public void tearDown() {
+ session.close();
+ }
+
+ @Test
+ public void should_list_authorized_projects_only() throws Exception {
+ project1 = newProject("ABCD", "Project One");
+ project2 = newProject("BCDE", "Project Two");
+ dbClient.componentDao().insert(session, project1, project2);
+
+ // user only sees project1
+ roleDao.insertUserRole(new UserRoleDto().setUserId(userId).setResourceId(project1.getId()).setRole(UserRole.USER), session);
+
+ associateProjectsWithProfile(session, xooP1, project1, project2);
+
+ session.commit();
+
+ newRequest().setParam("key", xooP1.getKey()).setParam("selected", "selected").execute().assertJson(this.getClass(), "authorized_selected.json");
+ }
+
+ @Test
+ public void should_paginate() throws Exception {
+ project1 = newProject("ABCD", "Project One");
+ project2 = newProject("BCDE", "Project Two");
+ project3 = newProject("CDEF", "Project Three");
+ project4 = newProject("DEFA", "Project Four");
+ dbClient.componentDao().insert(session, project1, project2, project3, project4);
+
+ addBrowsePermissionToAnyone(session, project1, project2, project3, project4);
+
+ associateProjectsWithProfile(session, xooP1, project1, project2, project3, project4);
+
+ session.commit();
+
+ newRequest().setParam("key", xooP1.getKey()).setParam("selected", "selected").setParam("pageSize", "2")
+ .execute().assertJson(this.getClass(), "selected_page1.json");
+ newRequest().setParam("key", xooP1.getKey()).setParam("selected", "selected").setParam("pageSize", "2").setParam("page", "2")
+ .execute().assertJson(this.getClass(), "selected_page2.json");
+ newRequest().setParam("key", xooP1.getKey()).setParam("selected", "selected").setParam("pageSize", "2").setParam("page", "3")
+ .execute().assertJson(this.getClass(), "empty.json");
+ newRequest().setParam("key", xooP1.getKey()).setParam("selected", "selected").setParam("pageSize", "2").setParam("page", "4")
+ .execute().assertJson(this.getClass(), "empty.json");
+ }
+
+ @Test
+ public void should_show_unselected() throws Exception {
+ project1 = newProject("ABCD", "Project One");
+ project2 = newProject("BCDE", "Project Two");
+ project3 = newProject("CDEF", "Project Three");
+ project4 = newProject("DEFA", "Project Four");
+ dbClient.componentDao().insert(session, project1, project2, project3, project4);
+
+ addBrowsePermissionToAnyone(session, project1, project2, project3, project4);
+
+ associateProjectsWithProfile(session, xooP1, project1, project2);
+
+ session.commit();
+
+ newRequest().setParam("key", xooP1.getKey()).setParam("selected", "deselected").execute().assertJson(this.getClass(), "deselected.json");
+ }
+
+ @Test
+ public void should_show_all() throws Exception {
+ project1 = newProject("ABCD", "Project One");
+ project2 = newProject("BCDE", "Project Two");
+ project3 = newProject("CDEF", "Project Three");
+ project4 = newProject("DEFA", "Project Four");
+ dbClient.componentDao().insert(session, project1, project2, project3, project4);
+
+ addBrowsePermissionToAnyone(session, project1, project2, project3, project4);
+
+ associateProjectsWithProfile(session, xooP1, project1, project2);
+ // project3 is associated with P2, must appear as not associated with xooP1
+ associateProjectsWithProfile(session, xooP2, project3);
+
+ session.commit();
+
+ newRequest().setParam("key", xooP1.getKey()).setParam("selected", "all").execute().assertJson(this.getClass(), "all.json");
+ }
+
+ @Test
+ public void should_filter_on_name() throws Exception {
+ project1 = newProject("ABCD", "Project One");
+ project2 = newProject("BCDE", "Project Two");
+ project3 = newProject("CDEF", "Project Three");
+ project4 = newProject("DEFA", "Project Four");
+ dbClient.componentDao().insert(session, project1, project2, project3, project4);
+
+ addBrowsePermissionToAnyone(session, project1, project2, project3, project4);
+
+ associateProjectsWithProfile(session, xooP1, project1, project2);
+
+ session.commit();
+
+ newRequest().setParam("key", xooP1.getKey()).setParam("selected", "all").setParam("query", "project t").execute().assertJson(this.getClass(), "all_filtered.json");
+ }
+
+ @Test(expected = NotFoundException.class)
+ public void should_fail_on_nonexistent_profile() throws Exception {
+ newRequest().setParam("key", "unknown").setParam("selected", "all").execute();
+ }
+
+ private void createProfiles() {
+ xooP1 = QProfileTesting.newXooP1();
+ xooP2 = QProfileTesting.newXooP2();
+ dbClient.qualityProfileDao().insert(session, xooP1, xooP2);
+ }
+
+ private TestRequest newRequest() {
+ return wsTester.newGetRequest("api/qualityprofiles", "projects");
+ }
+
+ private ComponentDto newProject(String uuid, String name) {
+ return ComponentTesting.newProjectDto(uuid).setName(name);
+ }
+
+ private void addBrowsePermissionToAnyone(DbSession session, ComponentDto... projects) {
+ for (ComponentDto project : projects) {
+ roleDao.insertGroupRole(new GroupRoleDto().setGroupId(null).setResourceId(project.getId()).setRole(UserRole.USER), session);
+ }
+ }
+
+ private void associateProjectsWithProfile(DbSession session, QualityProfileDto profile, ComponentDto... projects) {
+ for (ComponentDto project : projects) {
+ dbClient.qualityProfileDao().insertProjectProfileAssociation(project.uuid(), profile.getKey(), session);
+ }
+ }
+}
diff --git a/server/sonar-server/src/test/resources/org/sonar/server/qualityprofile/ws/QProfileProjectsActionTest/all.json b/server/sonar-server/src/test/resources/org/sonar/server/qualityprofile/ws/QProfileProjectsActionTest/all.json
new file mode 100644
index 00000000000..941b9cd15ee
--- /dev/null
+++ b/server/sonar-server/src/test/resources/org/sonar/server/qualityprofile/ws/QProfileProjectsActionTest/all.json
@@ -0,0 +1,27 @@
+{
+ "results":
+ [
+ {
+ "key": "DEFA",
+ "name": "Project Four",
+ "selected": false
+ },
+ {
+ "key": "ABCD",
+ "name": "Project One",
+ "selected": true
+ },
+ {
+ "key": "CDEF",
+ "name": "Project Three",
+ "selected": false
+ },
+ {
+ "key": "BCDE",
+ "name": "Project Two",
+ "selected": true
+ }
+ ],
+
+ "more": false
+} \ No newline at end of file
diff --git a/server/sonar-server/src/test/resources/org/sonar/server/qualityprofile/ws/QProfileProjectsActionTest/all_filtered.json b/server/sonar-server/src/test/resources/org/sonar/server/qualityprofile/ws/QProfileProjectsActionTest/all_filtered.json
new file mode 100644
index 00000000000..6eed637dcd8
--- /dev/null
+++ b/server/sonar-server/src/test/resources/org/sonar/server/qualityprofile/ws/QProfileProjectsActionTest/all_filtered.json
@@ -0,0 +1,17 @@
+{
+ "results":
+ [
+ {
+ "key": "CDEF",
+ "name": "Project Three",
+ "selected": false
+ },
+ {
+ "key": "BCDE",
+ "name": "Project Two",
+ "selected": true
+ }
+ ],
+
+ "more": false
+} \ No newline at end of file
diff --git a/server/sonar-server/src/test/resources/org/sonar/server/qualityprofile/ws/QProfileProjectsActionTest/authorized_selected.json b/server/sonar-server/src/test/resources/org/sonar/server/qualityprofile/ws/QProfileProjectsActionTest/authorized_selected.json
new file mode 100644
index 00000000000..52108a29446
--- /dev/null
+++ b/server/sonar-server/src/test/resources/org/sonar/server/qualityprofile/ws/QProfileProjectsActionTest/authorized_selected.json
@@ -0,0 +1,12 @@
+{
+ "results":
+ [
+ {
+ "key": "ABCD",
+ "name": "Project One",
+ "selected": true
+ }
+ ],
+
+ "more": false
+} \ No newline at end of file
diff --git a/server/sonar-server/src/test/resources/org/sonar/server/qualityprofile/ws/QProfileProjectsActionTest/deselected.json b/server/sonar-server/src/test/resources/org/sonar/server/qualityprofile/ws/QProfileProjectsActionTest/deselected.json
new file mode 100644
index 00000000000..c5adddd68ff
--- /dev/null
+++ b/server/sonar-server/src/test/resources/org/sonar/server/qualityprofile/ws/QProfileProjectsActionTest/deselected.json
@@ -0,0 +1,17 @@
+{
+ "results":
+ [
+ {
+ "key": "DEFA",
+ "name": "Project Four",
+ "selected": false
+ },
+ {
+ "key": "CDEF",
+ "name": "Project Three",
+ "selected": false
+ }
+ ],
+
+ "more": false
+} \ No newline at end of file
diff --git a/server/sonar-server/src/test/resources/org/sonar/server/qualityprofile/ws/QProfileProjectsActionTest/empty.json b/server/sonar-server/src/test/resources/org/sonar/server/qualityprofile/ws/QProfileProjectsActionTest/empty.json
new file mode 100644
index 00000000000..cea4e2ee3c4
--- /dev/null
+++ b/server/sonar-server/src/test/resources/org/sonar/server/qualityprofile/ws/QProfileProjectsActionTest/empty.json
@@ -0,0 +1,6 @@
+{
+ "results":
+ [],
+
+ "more": false
+} \ No newline at end of file
diff --git a/server/sonar-server/src/test/resources/org/sonar/server/qualityprofile/ws/QProfileProjectsActionTest/selected_page1.json b/server/sonar-server/src/test/resources/org/sonar/server/qualityprofile/ws/QProfileProjectsActionTest/selected_page1.json
new file mode 100644
index 00000000000..803a536e742
--- /dev/null
+++ b/server/sonar-server/src/test/resources/org/sonar/server/qualityprofile/ws/QProfileProjectsActionTest/selected_page1.json
@@ -0,0 +1,17 @@
+{
+ "results":
+ [
+ {
+ "key": "DEFA",
+ "name": "Project Four",
+ "selected": true
+ },
+ {
+ "key": "ABCD",
+ "name": "Project One",
+ "selected": true
+ }
+ ],
+
+ "more": true
+} \ No newline at end of file
diff --git a/server/sonar-server/src/test/resources/org/sonar/server/qualityprofile/ws/QProfileProjectsActionTest/selected_page2.json b/server/sonar-server/src/test/resources/org/sonar/server/qualityprofile/ws/QProfileProjectsActionTest/selected_page2.json
new file mode 100644
index 00000000000..1092ac3e4e8
--- /dev/null
+++ b/server/sonar-server/src/test/resources/org/sonar/server/qualityprofile/ws/QProfileProjectsActionTest/selected_page2.json
@@ -0,0 +1,17 @@
+{
+ "results":
+ [
+ {
+ "key": "CDEF",
+ "name": "Project Three",
+ "selected": true
+ },
+ {
+ "key": "BCDE",
+ "name": "Project Two",
+ "selected": true
+ }
+ ],
+
+ "more": false
+} \ No newline at end of file
diff --git a/sonar-core/src/main/java/org/sonar/core/qualityprofile/db/ProjectQprofileAssociationDto.java b/sonar-core/src/main/java/org/sonar/core/qualityprofile/db/ProjectQprofileAssociationDto.java
new file mode 100644
index 00000000000..e8f0d3c62b0
--- /dev/null
+++ b/sonar-core/src/main/java/org/sonar/core/qualityprofile/db/ProjectQprofileAssociationDto.java
@@ -0,0 +1,51 @@
+/*
+ * 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.core.qualityprofile.db;
+
+import javax.annotation.CheckForNull;
+
+public class ProjectQprofileAssociationDto {
+
+ private Long projectId;
+ private String projectUuid;
+ private String projectName;
+ private String profileKey;
+
+ public Long getProjectId() {
+ return projectId;
+ }
+
+ public String getProjectUuid() {
+ return projectUuid;
+ }
+
+ public String getProjectName() {
+ return projectName;
+ }
+
+ @CheckForNull
+ public String getProfileKey() {
+ return profileKey;
+ }
+
+ public boolean isAssociated() {
+ return profileKey != null;
+ }
+}
diff --git a/sonar-core/src/main/java/org/sonar/core/qualityprofile/db/QualityProfileDao.java b/sonar-core/src/main/java/org/sonar/core/qualityprofile/db/QualityProfileDao.java
index 4e39c7d5d9d..81b7ddbf24b 100644
--- a/sonar-core/src/main/java/org/sonar/core/qualityprofile/db/QualityProfileDao.java
+++ b/sonar-core/src/main/java/org/sonar/core/qualityprofile/db/QualityProfileDao.java
@@ -30,6 +30,7 @@ import org.sonar.core.persistence.DbSession;
import org.sonar.core.persistence.MyBatis;
import javax.annotation.CheckForNull;
+import javax.annotation.Nullable;
import java.util.Date;
import java.util.List;
@@ -334,4 +335,23 @@ public class QualityProfileDao implements ServerComponent, DaoComponent {
public void deleteAllProjectProfileAssociation(String profileKey, DbSession session) {
session.getMapper(QualityProfileMapper.class).deleteAllProjectProfileAssociation(profileKey);
}
+
+ public List<ProjectQprofileAssociationDto> selectSelectedProjects(String profileKey, @Nullable String query, DbSession session) {
+ String nameQuery = sqlQueryString(query);
+ return session.getMapper(QualityProfileMapper.class).selectSelectedProjects(profileKey, nameQuery);
+ }
+
+ public List<ProjectQprofileAssociationDto> selectDeselectedProjects(String profileKey, @Nullable String query, DbSession session) {
+ String nameQuery = sqlQueryString(query);
+ return session.getMapper(QualityProfileMapper.class).selectDeselectedProjects(profileKey, nameQuery);
+ }
+
+ public List<ProjectQprofileAssociationDto> selectProjectAssociations(String profileKey, @Nullable String query, DbSession session) {
+ String nameQuery = sqlQueryString(query);
+ return session.getMapper(QualityProfileMapper.class).selectProjectAssociations(profileKey, nameQuery);
+ }
+
+ private String sqlQueryString(String query) {
+ return query == null ? "%" : "%" + query.toUpperCase() + "%";
+ }
}
diff --git a/sonar-core/src/main/java/org/sonar/core/qualityprofile/db/QualityProfileMapper.java b/sonar-core/src/main/java/org/sonar/core/qualityprofile/db/QualityProfileMapper.java
index bbfda6f5a9c..940c9adce99 100644
--- a/sonar-core/src/main/java/org/sonar/core/qualityprofile/db/QualityProfileMapper.java
+++ b/sonar-core/src/main/java/org/sonar/core/qualityprofile/db/QualityProfileMapper.java
@@ -78,4 +78,10 @@ public interface QualityProfileMapper {
void deleteProjectProfileAssociation(@Param("projectUuid") String projectUuid, @Param("profileKey") String profileKey);
void deleteAllProjectProfileAssociation(@Param("profileKey") String profileKey);
+
+ List<ProjectQprofileAssociationDto> selectSelectedProjects(@Param("profileKey") String profileKey, @Param("nameQuery") String nameQuery);
+
+ List<ProjectQprofileAssociationDto> selectDeselectedProjects(@Param("profileKey") String profileKey, @Param("nameQuery") String nameQuery);
+
+ List<ProjectQprofileAssociationDto> selectProjectAssociations(@Param("profileKey") String profileKey, @Param("nameQuery") String nameQuery);
}
diff --git a/sonar-core/src/main/resources/org/sonar/core/qualityprofile/db/QualityProfileMapper.xml b/sonar-core/src/main/resources/org/sonar/core/qualityprofile/db/QualityProfileMapper.xml
index 1b244cf4011..3221d9bbf3d 100644
--- a/sonar-core/src/main/resources/org/sonar/core/qualityprofile/db/QualityProfileMapper.xml
+++ b/sonar-core/src/main/resources/org/sonar/core/qualityprofile/db/QualityProfileMapper.xml
@@ -92,7 +92,7 @@
AND p.language=#{language}
</select>
- <select id="selectProjects" parameterType="Integer" resultType="Component">
+ <select id="selectProjects" resultType="Component">
SELECT projects.id as id, projects.name as name, projects.kee as kee, projects.uuid as uuid
FROM projects projects
JOIN project_qprofiles pp ON pp.project_uuid = projects.uuid
@@ -103,6 +103,43 @@
</where>
</select>
+ <select id="selectSelectedProjects" resultType="org.sonar.core.qualityprofile.db.ProjectQprofileAssociationDto">
+ SELECT pp.id as id, pj.id as projectId, pj.uuid as projectUuid, pj.name as projectName, pp.profile_key as profileKey
+ FROM projects pj
+ JOIN project_qprofiles pp ON pp.project_uuid = pj.uuid
+ AND pp.profile_key = #{profileKey}
+ <where>
+ AND pj.scope='PRJ' AND pj.qualifier='TRK'
+ AND UPPER(pj.name) LIKE #{nameQuery}
+ </where>
+ ORDER BY pj.name ASC
+ </select>
+
+ <select id="selectDeselectedProjects" resultType="org.sonar.core.qualityprofile.db.ProjectQprofileAssociationDto">
+ SELECT pp.id as id, pj.id as projectId, pj.uuid as projectUuid, pj.name as projectName, pp.profile_key as profileKey
+ FROM projects pj
+ LEFT JOIN project_qprofiles pp ON pp.project_uuid = pj.uuid
+ AND pp.profile_key = #{profileKey}
+ <where>
+ AND pj.scope='PRJ' AND pj.qualifier='TRK'
+ AND UPPER(pj.name) LIKE #{nameQuery}
+ AND pp.profile_key IS NULL
+ </where>
+ ORDER BY pj.name ASC
+ </select>
+
+ <select id="selectProjectAssociations" resultType="org.sonar.core.qualityprofile.db.ProjectQprofileAssociationDto">
+ SELECT pp.id as id, pj.id as projectId, pj.uuid as projectUuid, pj.name as projectName, pp.profile_key as profileKey
+ FROM projects pj
+ LEFT JOIN project_qprofiles pp ON pp.project_uuid = pj.uuid
+ AND pp.profile_key = #{profileKey}
+ <where>
+ AND pj.scope='PRJ' AND pj.qualifier='TRK'
+ AND UPPER(pj.name) LIKE #{nameQuery}
+ </where>
+ ORDER BY pj.name ASC
+ </select>
+
<select id="countProjects" parameterType="Integer" resultType="Integer">
SELECT count(projects.id)
FROM projects projects