]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-5921 WS to list project/profile associations
authorJean-Baptiste Lievremont <jean-baptiste.lievremont@sonarsource.com>
Tue, 31 Mar 2015 14:55:55 +0000 (16:55 +0200)
committerJean-Baptiste Lievremont <jean-baptiste.lievremont@sonarsource.com>
Fri, 3 Apr 2015 09:55:08 +0000 (11:55 +0200)
14 files changed:
server/sonar-server/src/main/java/org/sonar/server/platform/ServerComponents.java
server/sonar-server/src/main/java/org/sonar/server/qualityprofile/ws/QProfileProjectsAction.java [new file with mode: 0644]
server/sonar-server/src/test/java/org/sonar/server/qualityprofile/ws/QProfileProjectsActionTest.java [new file with mode: 0644]
server/sonar-server/src/test/resources/org/sonar/server/qualityprofile/ws/QProfileProjectsActionTest/all.json [new file with mode: 0644]
server/sonar-server/src/test/resources/org/sonar/server/qualityprofile/ws/QProfileProjectsActionTest/all_filtered.json [new file with mode: 0644]
server/sonar-server/src/test/resources/org/sonar/server/qualityprofile/ws/QProfileProjectsActionTest/authorized_selected.json [new file with mode: 0644]
server/sonar-server/src/test/resources/org/sonar/server/qualityprofile/ws/QProfileProjectsActionTest/deselected.json [new file with mode: 0644]
server/sonar-server/src/test/resources/org/sonar/server/qualityprofile/ws/QProfileProjectsActionTest/empty.json [new file with mode: 0644]
server/sonar-server/src/test/resources/org/sonar/server/qualityprofile/ws/QProfileProjectsActionTest/selected_page1.json [new file with mode: 0644]
server/sonar-server/src/test/resources/org/sonar/server/qualityprofile/ws/QProfileProjectsActionTest/selected_page2.json [new file with mode: 0644]
sonar-core/src/main/java/org/sonar/core/qualityprofile/db/ProjectQprofileAssociationDto.java [new file with mode: 0644]
sonar-core/src/main/java/org/sonar/core/qualityprofile/db/QualityProfileDao.java
sonar-core/src/main/java/org/sonar/core/qualityprofile/db/QualityProfileMapper.java
sonar-core/src/main/resources/org/sonar/core/qualityprofile/db/QualityProfileMapper.xml

index eef1ee231407db4b3d85b5159a78150471350cef..38d5d1469fd06451dbe3d80b2474df18d0ed48dc 100644 (file)
@@ -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 (file)
index 0000000..d351b0f
--- /dev/null
@@ -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 (file)
index 0000000..9b73c6a
--- /dev/null
@@ -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 (file)
index 0000000..941b9cd
--- /dev/null
@@ -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 (file)
index 0000000..6eed637
--- /dev/null
@@ -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 (file)
index 0000000..52108a2
--- /dev/null
@@ -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 (file)
index 0000000..c5adddd
--- /dev/null
@@ -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 (file)
index 0000000..cea4e2e
--- /dev/null
@@ -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 (file)
index 0000000..803a536
--- /dev/null
@@ -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 (file)
index 0000000..1092ac3
--- /dev/null
@@ -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 (file)
index 0000000..e8f0d3c
--- /dev/null
@@ -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;
+  }
+}
index 4e39c7d5d9ddacc18a1a5f3403068a9c35ba13ad..81b7ddbf24b45d9b12f1c2774360ee1cc222f603 100644 (file)
@@ -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() + "%";
+  }
 }
index bbfda6f5a9ccf18cd6c2e5b8001d2e27a0bad7d3..940c9adce99241e21ca58803e58b868affab2f11 100644 (file)
@@ -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);
 }
index 1b244cf4011d497aac72ad47cffe2379f90abcc3..3221d9bbf3dd8f807e17a005fc55749181222899 100644 (file)
@@ -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
     </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