diff options
author | Jean-Baptiste Lievremont <jean-baptiste.lievremont@sonarsource.com> | 2015-03-31 16:55:55 +0200 |
---|---|---|
committer | Jean-Baptiste Lievremont <jean-baptiste.lievremont@sonarsource.com> | 2015-04-03 11:55:08 +0200 |
commit | 7bb307b1ca7d20809b38cd9556b6b34300b9113b (patch) | |
tree | e04ee07a6f32e560b3e9c99aa72adfbfa1b5a45c | |
parent | 83da0fcf581c31768ba322246da4079d23fa20bd (diff) | |
download | sonarqube-7bb307b1ca7d20809b38cd9556b6b34300b9113b.tar.gz sonarqube-7bb307b1ca7d20809b38cd9556b6b34300b9113b.zip |
SONAR-5921 WS to list project/profile associations
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 |