Browse Source

SONAR-18856 Refactor favorites and properties

tags/10.1.0.73491
Duarte Meneses 1 year ago
parent
commit
46effb33d7
48 changed files with 729 additions and 1039 deletions
  1. 1
    0
      server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/analysis/ProjectConfigurationFactory.java
  2. 7
    6
      server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/analysis/ProjectConfigurationFactoryTest.java
  3. 56
    0
      server/sonar-db-dao/src/it/java/org/sonar/db/project/ProjectDaoIT.java
  4. 15
    60
      server/sonar-db-dao/src/it/java/org/sonar/db/property/PropertiesDaoIT.java
  5. 2
    0
      server/sonar-db-dao/src/main/java/org/sonar/db/MyBatis.java
  6. 77
    0
      server/sonar-db-dao/src/main/java/org/sonar/db/entity/EntityDto.java
  7. 11
    47
      server/sonar-db-dao/src/main/java/org/sonar/db/portfolio/PortfolioDto.java
  8. 30
    3
      server/sonar-db-dao/src/main/java/org/sonar/db/project/ProjectDao.java
  9. 2
    51
      server/sonar-db-dao/src/main/java/org/sonar/db/project/ProjectDto.java
  10. 12
    0
      server/sonar-db-dao/src/main/java/org/sonar/db/project/ProjectMapper.java
  11. 1
    14
      server/sonar-db-dao/src/main/java/org/sonar/db/property/PropertiesDao.java
  12. 0
    8
      server/sonar-db-dao/src/main/java/org/sonar/db/property/PropertiesMapper.java
  13. 60
    0
      server/sonar-db-dao/src/main/resources/org/sonar/db/project/ProjectMapper.xml
  14. 3
    59
      server/sonar-db-dao/src/main/resources/org/sonar/db/property/PropertiesMapper.xml
  15. 8
    7
      server/sonar-db-dao/src/testFixtures/java/org/sonar/db/favorite/FavoriteDbTester.java
  16. 9
    9
      server/sonar-db-dao/src/testFixtures/java/org/sonar/db/property/PropertyDbTester.java
  17. 4
    3
      server/sonar-db-dao/src/testFixtures/java/org/sonar/db/property/PropertyTesting.java
  18. 17
    15
      server/sonar-server-common/src/it/java/org/sonar/server/favorite/FavoriteUpdaterIT.java
  19. 8
    7
      server/sonar-server-common/src/main/java/org/sonar/server/favorite/FavoriteUpdater.java
  20. 0
    76
      server/sonar-server-common/src/main/java/org/sonar/server/notification/DefaultNotificationManager.java
  21. 0
    17
      server/sonar-server-common/src/main/java/org/sonar/server/notification/NotificationManager.java
  22. 0
    119
      server/sonar-server-common/src/test/java/org/sonar/server/notification/DefaultNotificationManagerTest.java
  23. 30
    1
      server/sonar-webserver-auth/src/main/java/org/sonar/server/user/AbstractUserSession.java
  24. 12
    0
      server/sonar-webserver-auth/src/main/java/org/sonar/server/user/ServerUserSession.java
  25. 17
    1
      server/sonar-webserver-auth/src/main/java/org/sonar/server/user/ThreadLocalUserSession.java
  26. 8
    0
      server/sonar-webserver-auth/src/main/java/org/sonar/server/user/UserSession.java
  27. 18
    1
      server/sonar-webserver-auth/src/testFixtures/java/org/sonar/server/tester/UserSessionRule.java
  28. 13
    15
      server/sonar-webserver-webapi/src/it/java/org/sonar/server/ce/queue/ReportSubmitterIT.java
  29. 9
    8
      server/sonar-webserver-webapi/src/it/java/org/sonar/server/component/ComponentUpdaterIT.java
  30. 2
    1
      server/sonar-webserver-webapi/src/it/java/org/sonar/server/component/ws/AppActionIT.java
  31. 42
    43
      server/sonar-webserver-webapi/src/it/java/org/sonar/server/component/ws/SuggestionsActionIT.java
  32. 6
    29
      server/sonar-webserver-webapi/src/it/java/org/sonar/server/favorite/ws/AddActionIT.java
  33. 7
    13
      server/sonar-webserver-webapi/src/it/java/org/sonar/server/favorite/ws/RemoveActionIT.java
  34. 17
    22
      server/sonar-webserver-webapi/src/it/java/org/sonar/server/favorite/ws/SearchActionIT.java
  35. 4
    3
      server/sonar-webserver-webapi/src/it/java/org/sonar/server/project/ws/CreateActionIT.java
  36. 40
    120
      server/sonar-webserver-webapi/src/it/java/org/sonar/server/setting/ws/ResetActionIT.java
  37. 6
    6
      server/sonar-webserver-webapi/src/it/java/org/sonar/server/setting/ws/SetActionIT.java
  38. 10
    9
      server/sonar-webserver-webapi/src/it/java/org/sonar/server/setting/ws/SettingsUpdaterIT.java
  39. 18
    114
      server/sonar-webserver-webapi/src/it/java/org/sonar/server/setting/ws/ValuesActionIT.java
  40. 13
    1
      server/sonar-webserver-webapi/src/main/java/org/sonar/server/component/ComponentUpdater.java
  41. 41
    70
      server/sonar-webserver-webapi/src/main/java/org/sonar/server/component/ws/SuggestionsAction.java
  42. 7
    6
      server/sonar-webserver-webapi/src/main/java/org/sonar/server/favorite/FavoriteFinder.java
  43. 8
    4
      server/sonar-webserver-webapi/src/main/java/org/sonar/server/favorite/ws/AddAction.java
  44. 11
    10
      server/sonar-webserver-webapi/src/main/java/org/sonar/server/favorite/ws/SearchAction.java
  45. 11
    5
      server/sonar-webserver-webapi/src/main/java/org/sonar/server/setting/ws/ResetAction.java
  46. 23
    20
      server/sonar-webserver-webapi/src/main/java/org/sonar/server/setting/ws/SetAction.java
  47. 12
    17
      server/sonar-webserver-webapi/src/main/java/org/sonar/server/setting/ws/SettingValidations.java
  48. 21
    19
      server/sonar-webserver-webapi/src/main/java/org/sonar/server/setting/ws/SettingsUpdater.java

+ 1
- 0
server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/analysis/ProjectConfigurationFactory.java View File

@@ -42,6 +42,7 @@ public class ProjectConfigurationFactory {
Settings projectSettings = new ChildSettings(globalSettings);
addSettings(projectSettings, projectUuid);
if (!projectUuid.equals(branchUuid)) {
// TODO not supported?
addSettings(projectSettings, branchUuid);
}
return new ConfigurationBridge(projectSettings);

+ 7
- 6
server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/analysis/ProjectConfigurationFactoryTest.java View File

@@ -25,6 +25,7 @@ import org.sonar.api.config.Configuration;
import org.sonar.api.config.internal.MapSettings;
import org.sonar.db.DbTester;
import org.sonar.db.component.ComponentDto;
import org.sonar.db.project.ProjectDto;

import static org.assertj.core.api.Assertions.assertThat;
import static org.sonar.db.property.PropertyTesting.newComponentPropertyDto;
@@ -46,13 +47,13 @@ public class ProjectConfigurationFactoryTest {

@Test
public void return_project_settings() {
ComponentDto project = db.components().insertPrivateProject().getMainBranchComponent();
db.properties().insertProperties(null, project.getKey(), project.name(), project.qualifier(),
ProjectDto project = db.components().insertPrivateProject().getProjectDto();
db.properties().insertProperties(null, project.getKey(), project.getName(), project.getQualifier(),
newComponentPropertyDto(project).setKey("1").setValue("val1"),
newComponentPropertyDto(project).setKey("2").setValue("val2"),
newComponentPropertyDto(project).setKey("3").setValue("val3"));

Configuration config = underTest.newProjectConfiguration(project.uuid(), project.uuid());
Configuration config = underTest.newProjectConfiguration(project.getUuid(), project.getUuid());

assertThat(config.get("1")).hasValue("val1");
assertThat(config.get("2")).hasValue("val2");
@@ -62,11 +63,11 @@ public class ProjectConfigurationFactoryTest {
@Test
public void project_settings_override_global_settings() {
settings.setProperty("key", "value");
ComponentDto project = db.components().insertPrivateProject().getMainBranchComponent();
db.properties().insertProperties(null, project.getKey(), project.name(), project.qualifier(),
ProjectDto project = db.components().insertPrivateProject().getProjectDto();
db.properties().insertProperties(null, project.getKey(), project.getName(), project.getQualifier(),
newComponentPropertyDto(project).setKey("key").setValue("value2"));

Configuration projectConfig = underTest.newProjectConfiguration(project.uuid(), project.uuid());
Configuration projectConfig = underTest.newProjectConfiguration(project.getUuid(), project.getUuid());

assertThat(projectConfig.get("key")).hasValue("value2");
}

+ 56
- 0
server/sonar-db-dao/src/it/java/org/sonar/db/project/ProjectDaoIT.java View File

@@ -44,8 +44,10 @@ import org.sonar.db.audit.NoOpAuditPersister;
import org.sonar.db.component.BranchDto;
import org.sonar.db.component.ComponentDto;
import org.sonar.db.component.ProjectData;
import org.sonar.db.entity.EntityDto;
import org.sonar.db.measure.LiveMeasureDto;
import org.sonar.db.metric.MetricDto;
import org.sonar.db.portfolio.PortfolioDto;
import org.sonar.db.qualityprofile.QProfileDto;

import static org.apache.commons.lang.math.RandomUtils.nextInt;
@@ -327,6 +329,60 @@ public class ProjectDaoIT {
assertThat(projectUuids).containsExactlyInAnyOrder(project.projectUuid(), project2.projectUuid());
}

@Test
public void selectEntitiesByKeys_shouldReturnAllEntities() {
ProjectData application = db.components().insertPrivateApplication();
ProjectData project = db.components().insertPrivateProject();
PortfolioDto portfolio = db.components().insertPrivatePortfolioDto();

assertThat(projectDao.selectEntitiesByKeys(db.getSession(), List.of(application.projectKey(), project.projectKey(), portfolio.getKey(), "unknown")))
.extracting(EntityDto::getUuid)
.containsOnly(application.projectUuid(), project.projectUuid(), portfolio.getUuid());
}

@Test
public void selectEntitiesByUuids_shouldReturnAllEntities() {
ProjectData application = db.components().insertPrivateApplication();
ProjectData project = db.components().insertPrivateProject();
PortfolioDto portfolio = db.components().insertPrivatePortfolioDto();

assertThat(projectDao.selectEntitiesByUuids(db.getSession(), List.of(application.projectUuid(), project.projectUuid(), portfolio.getUuid(), "unknown")))
.extracting(EntityDto::getKey)
.containsOnly(application.projectKey(), project.projectKey(), portfolio.getKey());
}

@Test
public void selectEntityByUuid_shouldReturnAllEntities() {
ProjectData application = db.components().insertPrivateApplication();
ProjectData project = db.components().insertPrivateProject();
PortfolioDto portfolio = db.components().insertPrivatePortfolioDto();

assertThat(projectDao.selectEntityByUuid(db.getSession(), application.projectUuid()).get().getKey()).isEqualTo(application.projectKey());
assertThat(projectDao.selectEntityByUuid(db.getSession(), project.projectUuid()).get().getKey()).isEqualTo(project.projectKey());
assertThat(projectDao.selectEntityByUuid(db.getSession(), portfolio.getUuid()).get().getKey()).isEqualTo(portfolio.getKey());
}

@Test
public void selectEntityByUuid_whenNoMatch_shouldReturnEmpty() {
assertThat(projectDao.selectEntityByUuid(db.getSession(), "unknown")).isEmpty();
}

@Test
public void selectEntityByKey_shouldReturnAllEntities() {
ProjectData application = db.components().insertPrivateApplication();
ProjectData project = db.components().insertPrivateProject();
PortfolioDto portfolio = db.components().insertPrivatePortfolioDto();

assertThat(projectDao.selectEntityByKey(db.getSession(), application.projectKey()).get().getUuid()).isEqualTo(application.projectUuid());
assertThat(projectDao.selectEntityByKey(db.getSession(), project.projectKey()).get().getUuid()).isEqualTo(project.projectUuid());
assertThat(projectDao.selectEntityByKey(db.getSession(), portfolio.getKey()).get().getUuid()).isEqualTo(portfolio.getUuid());
}

@Test
public void selectEntityByKey_whenNoMatch_shouldReturnEmpty() {
assertThat(projectDao.selectEntityByKey(db.getSession(), "unknown")).isEmpty();
}

private void insertDefaultQualityProfile(String language) {
QProfileDto profile = db.qualityProfiles().insert(qp -> qp.setIsBuiltIn(true).setLanguage(language));
db.qualityProfiles().setAsDefault(profile);

+ 15
- 60
server/sonar-db-dao/src/it/java/org/sonar/db/property/PropertiesDaoIT.java View File

@@ -45,6 +45,7 @@ import org.sonar.db.EmailSubscriberDto;
import org.sonar.db.audit.AuditPersister;
import org.sonar.db.component.ComponentDto;
import org.sonar.db.component.ComponentTesting;
import org.sonar.db.project.ProjectDto;
import org.sonar.db.user.UserDto;

import static com.google.common.collect.ImmutableSet.of;
@@ -88,52 +89,6 @@ public class PropertiesDaoIT {
when(auditPersister.isTrackedProperty(anyString())).thenReturn(true);
}

@Test
public void shouldFindUsersForNotification() {
ComponentDto project1 = insertPrivateProject("uuid_45");
ComponentDto project2 = insertPrivateProject("uuid_56");
UserDto user1 = db.users().insertUser(u -> u.setLogin("user1"));
UserDto user2 = db.users().insertUser(u -> u.setLogin("user2"));
UserDto user3 = db.users().insertUser(u -> u.setLogin("user3"));
insertProperty("notification.NewViolations.Email", "true", project1.uuid(), user2.getUuid(), user2.getLogin(),
project1.getKey(), project1.name());
insertProperty("notification.NewViolations.Twitter", "true", null, user3.getUuid(), user3.getLogin(),
null, null);
insertProperty("notification.NewViolations.Twitter", "true", project2.uuid(), user1.getUuid(), user1.getLogin(),
project2.getKey(), project2.name());
insertProperty("notification.NewViolations.Twitter", "true", project1.uuid(), user2.getUuid(), user2.getLogin(),
project1.getKey(), project1.name());
insertProperty("notification.NewViolations.Twitter", "true", project2.uuid(), user3.getUuid(), user3.getLogin(),
project2.getKey(), project2.name());
db.users().insertProjectPermissionOnUser(user2, UserRole.USER, project1);
db.users().insertProjectPermissionOnUser(user3, UserRole.USER, project2);
db.users().insertProjectPermissionOnUser(user1, UserRole.USER, project2);

assertThat(underTest.findUsersForNotification("NewViolations", "Email", null))
.isEmpty();

assertThat(underTest.findUsersForNotification("NewViolations", "Email", "uuid_78"))
.isEmpty();

assertThat(underTest.findUsersForNotification("NewViolations", "Email", project1.getKey()))
.containsOnly(new Subscriber("user2", false));

assertThat(underTest.findUsersForNotification("NewViolations", "Email", project2.getKey()))
.isEmpty();

assertThat(underTest.findUsersForNotification("NewViolations", "Twitter", null))
.containsOnly(new Subscriber("user3", true));

assertThat(underTest.findUsersForNotification("NewViolations", "Twitter", "uuid_78"))
.containsOnly(new Subscriber("user3", true));

assertThat(underTest.findUsersForNotification("NewViolations", "Twitter", project1.getKey()))
.containsOnly(new Subscriber("user2", false), new Subscriber("user3", true));

assertThat(underTest.findUsersForNotification("NewViolations", "Twitter", project2.getKey()))
.containsOnly(new Subscriber("user1", false), new Subscriber("user3", true), new Subscriber("user3", false));
}

@Test
public void hasNotificationSubscribers() {
UserDto user1 = db.users().insertUser(u -> u.setLogin("user1"));
@@ -615,31 +570,31 @@ public class PropertiesDaoIT {

@Test
public void select_properties_by_keys_and_component_ids() {
ComponentDto project = db.components().insertPrivateProject().getMainBranchComponent();
ComponentDto project2 = db.components().insertPrivateProject().getMainBranchComponent();
ProjectDto project = db.components().insertPrivateProject().getProjectDto();
ProjectDto project2 = db.components().insertPrivateProject().getProjectDto();
UserDto user = db.users().insertUser();

String key = "key";
String anotherKey = "anotherKey";
insertProperties(null, null, null, newGlobalPropertyDto().setKey(key));
insertProperties(null, project.getKey(), project.name(), newComponentPropertyDto(project).setKey(key));
insertProperties(null, project2.getKey(), project2.name(), newComponentPropertyDto(project2).setKey(key),
insertProperties(null, project.getKey(), project.getName(), newComponentPropertyDto(project).setKey(key));
insertProperties(null, project2.getKey(), project2.getName(), newComponentPropertyDto(project2).setKey(key),
newComponentPropertyDto(project2).setKey(anotherKey));
insertProperties(user.getLogin(), null, null, newUserPropertyDto(user).setKey(key));

assertThat(underTest.selectPropertiesByKeysAndComponentUuids(session, newHashSet(key), newHashSet(project.uuid())))
.extracting("key", "componentUuid").containsOnly(tuple(key, project.uuid()));
assertThat(underTest.selectPropertiesByKeysAndComponentUuids(session, newHashSet(key), newHashSet(project.uuid(), project2.uuid())))
assertThat(underTest.selectPropertiesByKeysAndComponentUuids(session, newHashSet(key), newHashSet(project.getUuid())))
.extracting("key", "componentUuid").containsOnly(tuple(key, project.getUuid()));
assertThat(underTest.selectPropertiesByKeysAndComponentUuids(session, newHashSet(key), newHashSet(project.getUuid(), project2.getUuid())))
.extracting("key", "componentUuid").containsOnly(
tuple(key, project.uuid()),
tuple(key, project2.uuid()));
assertThat(underTest.selectPropertiesByKeysAndComponentUuids(session, newHashSet(key, anotherKey), newHashSet(project.uuid(), project2.uuid())))
tuple(key, project.getUuid()),
tuple(key, project2.getUuid()));
assertThat(underTest.selectPropertiesByKeysAndComponentUuids(session, newHashSet(key, anotherKey), newHashSet(project.getUuid(), project2.getUuid())))
.extracting("key", "componentUuid").containsOnly(
tuple(key, project.uuid()),
tuple(key, project2.uuid()),
tuple(anotherKey, project2.uuid()));
tuple(key, project.getUuid()),
tuple(key, project2.getUuid()),
tuple(anotherKey, project2.getUuid()));

assertThat(underTest.selectPropertiesByKeysAndComponentUuids(session, newHashSet("unknown"), newHashSet(project.uuid()))).isEmpty();
assertThat(underTest.selectPropertiesByKeysAndComponentUuids(session, newHashSet("unknown"), newHashSet(project.getUuid()))).isEmpty();
assertThat(underTest.selectPropertiesByKeysAndComponentUuids(session, newHashSet("key"), newHashSet("uuid123456789"))).isEmpty();
assertThat(underTest.selectPropertiesByKeysAndComponentUuids(session, newHashSet("unknown"), newHashSet("uuid123456789"))).isEmpty();
}

+ 2
- 0
server/sonar-db-dao/src/main/java/org/sonar/db/MyBatis.java View File

@@ -64,6 +64,7 @@ import org.sonar.db.component.UuidWithBranchUuidDto;
import org.sonar.db.component.ViewsSnapshotDto;
import org.sonar.db.duplication.DuplicationMapper;
import org.sonar.db.duplication.DuplicationUnitDto;
import org.sonar.db.entity.EntityDto;
import org.sonar.db.es.EsQueueMapper;
import org.sonar.db.event.EventComponentChangeMapper;
import org.sonar.db.event.EventDto;
@@ -195,6 +196,7 @@ public class MyBatis {
confBuilder.loadAlias("CeTaskCharacteristic", CeTaskCharacteristicDto.class);
confBuilder.loadAlias("Component", ComponentDto.class);
confBuilder.loadAlias("DuplicationUnit", DuplicationUnitDto.class);
confBuilder.loadAlias("Entity", EntityDto.class);
confBuilder.loadAlias("Event", EventDto.class);
confBuilder.loadAlias("ExternalGroup", ExternalGroupDto.class);
confBuilder.loadAlias("FilePathWithHash", FilePathWithHashDto.class);

+ 77
- 0
server/sonar-db-dao/src/main/java/org/sonar/db/entity/EntityDto.java View File

@@ -0,0 +1,77 @@
/*
* SonarQube
* Copyright (C) 2009-2023 SonarSource SA
* mailto:info AT sonarsource DOT com
*
* This program 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.
*
* This program 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.db.entity;

import java.util.Objects;

/**
* Represents a project, an application, a portfolio or a sub-portfolio.
* Entities are stored either in the projects or portfolios tables.
*/
public class EntityDto {
protected String kee;
protected String uuid;
protected String name;
protected String qualifier;
protected boolean isPrivate;

public String getKey() {
return kee;
}

public String getKee() {
return kee;
}

public String getUuid() {
return uuid;
}

/**
* Can be TRK, APP, VW or SVW
*/
public String getQualifier() {
return qualifier;
}

public String getName() {
return name;
}

public boolean isPrivate() {
return isPrivate;
}

@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (!(o instanceof EntityDto entityDto)) {
return false;
}
return Objects.equals(uuid, entityDto.uuid);
}

@Override
public int hashCode() {
return Objects.hash(uuid);
}
}

+ 11
- 47
server/sonar-db-dao/src/main/java/org/sonar/db/portfolio/PortfolioDto.java View File

@@ -19,20 +19,17 @@
*/
package org.sonar.db.portfolio;

import java.util.Objects;
import javax.annotation.CheckForNull;
import javax.annotation.Nullable;
import org.sonar.api.resources.Qualifiers;
import org.sonar.db.entity.EntityDto;

public class PortfolioDto {
public class PortfolioDto extends EntityDto {
public enum SelectionMode {
NONE, MANUAL, REGEXP, REST, TAGS
}

private String uuid;
private String kee;
private String name;
private String description;
private boolean isPrivate = false;
private String branchKey;

private String rootUuid;
@@ -79,6 +76,14 @@ public class PortfolioDto {
this.branchKey = branchKey;
}

@Override
public String getQualifier() {
if (isRoot()) {
return Qualifiers.VIEW;
}
return Qualifiers.SUBVIEW;
}

public String getSelectionMode() {
return selectionMode;
}
@@ -121,26 +126,11 @@ public class PortfolioDto {
return this;
}

public String getUuid() {
return uuid;
}

public PortfolioDto setUuid(String uuid) {
this.uuid = uuid;
return this;
}

/**
* This is the getter used by MyBatis mapper.
*/
public String getKee() {
return kee;
}

public String getKey() {
return getKee();
}

/**
* This is the setter used by MyBatis mapper.
*/
@@ -153,19 +143,11 @@ public class PortfolioDto {
return setKee(key);
}

public boolean isPrivate() {
return isPrivate;
}

public PortfolioDto setPrivate(boolean aPrivate) {
isPrivate = aPrivate;
return this;
}

public String getName() {
return name;
}

public PortfolioDto setName(String name) {
this.name = name;
return this;
@@ -180,22 +162,4 @@ public class PortfolioDto {
this.description = description;
return this;
}

@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
PortfolioDto that = (PortfolioDto) o;
return Objects.equals(uuid, that.uuid);
}

@Override
public int hashCode() {
return uuid != null ? uuid.hashCode() : 0;
}

}

+ 30
- 3
server/sonar-db-dao/src/main/java/org/sonar/db/project/ProjectDao.java View File

@@ -19,6 +19,7 @@
*/
package org.sonar.db.project;

import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Optional;
@@ -29,7 +30,9 @@ import org.sonar.db.Dao;
import org.sonar.db.DbSession;
import org.sonar.db.audit.AuditPersister;
import org.sonar.db.audit.model.ComponentNewValue;
import org.sonar.db.entity.EntityDto;

import static java.util.Collections.emptyList;
import static org.sonar.db.DatabaseUtils.executeLargeInputs;

public class ProjectDao implements Dao {
@@ -70,14 +73,14 @@ public class ProjectDao implements Dao {

public List<ProjectDto> selectProjectsByKeys(DbSession session, Set<String> keys) {
if (keys.isEmpty()) {
return Collections.emptyList();
return emptyList();
}
return mapper(session).selectProjectsByKeys(keys);
}

public List<ProjectDto> selectApplicationsByKeys(DbSession session, Set<String> keys) {
if (keys.isEmpty()) {
return Collections.emptyList();
return emptyList();
}

return executeLargeInputs(keys, partition -> mapper(session).selectApplicationsByKeys(partition));
@@ -101,7 +104,7 @@ public class ProjectDao implements Dao {

public List<ProjectDto> selectByUuids(DbSession session, Set<String> uuids) {
if (uuids.isEmpty()) {
return Collections.emptyList();
return emptyList();
}
return executeLargeInputs(uuids, partition -> mapper(session).selectByUuids(partition));
}
@@ -143,4 +146,28 @@ public class ProjectDao implements Dao {
public long getNclocSum(DbSession dbSession, @Nullable String projectUuidToExclude) {
return Optional.ofNullable(mapper(dbSession).getNclocSum(projectUuidToExclude)).orElse(0L);
}

public Optional<EntityDto> selectEntityByUuid(DbSession dbSession, String uuid) {
return Optional.ofNullable(mapper(dbSession).selectEntityByUuid(uuid));
}

public List<EntityDto> selectEntitiesByUuids(DbSession dbSession, Collection<String> uuids) {
if (uuids.isEmpty()) {
return emptyList();
}
return mapper(dbSession).selectEntitiesByUuids(uuids);
}

public Optional<EntityDto> selectEntityByKey(DbSession dbSession, String key) {
return Optional.ofNullable(mapper(dbSession).selectEntityByKey(key));
}

public List<EntityDto> selectEntitiesByKeys(DbSession dbSession, Collection<String> keys) {
if (keys.isEmpty()) {
return emptyList();
}
return mapper(dbSession).selectEntitiesByKeys(keys);
}


}

+ 2
- 51
server/sonar-db-dao/src/main/java/org/sonar/db/project/ProjectDto.java View File

@@ -24,18 +24,14 @@ import java.util.Objects;
import java.util.stream.Collectors;
import javax.annotation.CheckForNull;
import javax.annotation.Nullable;
import org.sonar.db.entity.EntityDto;

import static org.apache.commons.lang.StringUtils.trimToNull;
import static org.sonar.db.component.DbTagsReader.readDbTags;

public class ProjectDto {
public class ProjectDto extends EntityDto {
private static final String TAGS_SEPARATOR = ",";
private String uuid;
private String kee;
private String qualifier;
private String name;
private String description;
private boolean isPrivate = false;
private String tags;
private long createdAt;
private long updatedAt;
@@ -62,26 +58,11 @@ public class ProjectDto {
return this;
}

public String getUuid() {
return uuid;
}

public ProjectDto setUuid(String uuid) {
this.uuid = uuid;
return this;
}

/**
* This is the getter used by MyBatis mapper.
*/
public String getKee() {
return kee;
}

public String getKey() {
return getKee();
}

/**
* This is the setter used by MyBatis mapper.
*/
@@ -94,10 +75,6 @@ public class ProjectDto {
return setKee(key);
}

public boolean isPrivate() {
return isPrivate;
}

public ProjectDto setPrivate(boolean aPrivate) {
isPrivate = aPrivate;
return this;
@@ -127,10 +104,6 @@ public class ProjectDto {
return this;
}

public String getName() {
return name;
}

public ProjectDto setName(String name) {
this.name = name;
return this;
@@ -146,30 +119,8 @@ public class ProjectDto {
return this;
}

public String getQualifier() {
return qualifier;
}

public ProjectDto setQualifier(String qualifier) {
this.qualifier = qualifier;
return this;
}

@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
ProjectDto that = (ProjectDto) o;
return Objects.equals(uuid, that.uuid);
}

@Override
public int hashCode() {
return uuid != null ? uuid.hashCode() : 0;
}

}

+ 12
- 0
server/sonar-db-dao/src/main/java/org/sonar/db/project/ProjectMapper.java View File

@@ -21,10 +21,13 @@ package org.sonar.db.project;

import java.util.Collection;
import java.util.List;
import java.util.Optional;
import java.util.Set;
import javax.annotation.CheckForNull;
import javax.annotation.Nullable;
import org.apache.ibatis.annotations.Param;
import org.sonar.db.DbSession;
import org.sonar.db.entity.EntityDto;

public interface ProjectMapper {

@@ -72,4 +75,13 @@ public interface ProjectMapper {
@CheckForNull
Long getNclocSum(@Nullable @Param("projectUuidToExclude") String projectUuidToExclude);

@CheckForNull
EntityDto selectEntityByUuid(String uuid);

List<EntityDto> selectEntitiesByUuids(@Param("uuids") Collection<String> uuids);

@CheckForNull
EntityDto selectEntityByKey(String key);

List<EntityDto> selectEntitiesByKeys(@Param("keys") Collection<String> keys);
}

+ 1
- 14
server/sonar-db-dao/src/main/java/org/sonar/db/property/PropertiesDao.java View File

@@ -65,20 +65,6 @@ public class PropertiesDao implements Dao {
this.auditPersister = auditPersister;
}

/**
* Returns the logins of users who have subscribed to the given notification dispatcher with the given notification channel.
* If a resource ID is passed, the search is made on users who have specifically subscribed for the given resource.
* Note that {@link UserRole#USER} permission is not checked here, filter the results with
* {@link org.sonar.db.permission.AuthorizationDao#keepAuthorizedLoginsOnProject}
*
* @return the list of Subscriber (maybe be empty - obviously)
*/
public Set<Subscriber> findUsersForNotification(String notificationDispatcherKey, String notificationChannelKey, @Nullable String projectKey) {
try (DbSession session = mybatis.openSession(false)) {
return getMapper(session).findUsersForNotification(NOTIFICATION_PREFIX + notificationDispatcherKey + "." + notificationChannelKey, projectKey);
}
}

public Set<EmailSubscriberDto> findEmailSubscribersForNotification(DbSession dbSession, String notificationDispatcherKey, String notificationChannelKey,
@Nullable String projectKey) {
return getMapper(dbSession).findEmailRecipientsForNotification(NOTIFICATION_PREFIX + notificationDispatcherKey + "." + notificationChannelKey, projectKey, null);
@@ -171,6 +157,7 @@ public class PropertiesDao implements Dao {
return getMapper(session).selectByKeyAndMatchingValue(key, value);
}

//TODO
public List<PropertyDto> selectByKeyAndUserUuidAndComponentQualifier(DbSession session, String key, String userUuid, String qualifier) {
return getMapper(session).selectByKeyAndUserUuidAndComponentQualifier(key, userUuid, qualifier);
}

+ 0
- 8
server/sonar-db-dao/src/main/java/org/sonar/db/property/PropertiesMapper.java View File

@@ -27,8 +27,6 @@ import org.sonar.db.EmailSubscriberDto;

public interface PropertiesMapper {

Set<Subscriber> findUsersForNotification(@Param("notifKey") String notificationKey, @Nullable @Param("projectKey") String projectKey);

Set<EmailSubscriberDto> findEmailRecipientsForNotification(@Param("notifKey") String notificationKey, @Nullable @Param("projectKey") String projectKey,
@Nullable @Param("logins") List<String> logins);

@@ -48,10 +46,6 @@ public interface PropertiesMapper {

List<PropertyDto> selectByKeyAndMatchingValue(@Param("key") String key, @Param("value") String value);

List<String> selectUuidsByUser(@Param("userUuid") String userUuid);

List<String> selectIdsByMatchingLogin(@Param("login") String login, @Param("propertyKeys") List<String> propertyKeys);

void insertAsEmpty(@Param("uuid") String uuid, @Param("key") String key, @Nullable @Param("userUuid") String userUuid, @Nullable @Param("componentUuid") String componentUuid,
@Param("now") long now);

@@ -65,8 +59,6 @@ public interface PropertiesMapper {

int deleteProjectProperty(@Param("key") String key, @Param("componentUuid") String componentUuid);

int deleteProjectProperties(@Param("key") String key, @Param("value") String value);

int deleteGlobalProperty(@Param("key") String key);

int deleteByQuery(@Param("query") PropertyQuery query);

+ 60
- 0
server/sonar-db-dao/src/main/resources/org/sonar/db/project/ProjectMapper.xml View File

@@ -202,4 +202,64 @@
</if>
</select>

<sql id="entityProjectColumns">
p.uuid as uuid, p.kee as kee, p.name as name, p.private as isPrivate, p.qualifier as qualifier
</sql>

<sql id="entityPortfolioColumns">
p.uuid as uuid, p.kee as kee, p.name as name, p.private as isPrivate,
case when p.parent_uuid is null then 'VW' else 'SVW' end as qualifier
</sql>

<select id="selectEntityByUuid" parameterType="string" resultType="Entity">
(select <include refid="entityProjectColumns"/>
from projects p
where p.uuid = #{uuid,jdbcType=VARCHAR})
UNION
(select <include refid="entityPortfolioColumns"/>
from portfolios p
where p.uuid = #{uuid,jdbcType=VARCHAR})
</select>

<select id="selectEntitiesByUuids" resultType="Entity">
(select <include refid="entityProjectColumns"/>
from projects p
where p.uuid in
<foreach collection="uuids" open="(" close=")" item="uuid" separator=",">
#{uuid,jdbcType=VARCHAR}
</foreach>)
UNION
(select <include refid="entityPortfolioColumns"/>
from portfolios p
where p.uuid in
<foreach collection="uuids" open="(" close=")" item="uuid" separator=",">
#{uuid,jdbcType=VARCHAR}
</foreach>)
</select>

<select id="selectEntityByKey" parameterType="string" resultType="Entity">
(select <include refid="entityProjectColumns"/>
from projects p
where p.kee = #{key,jdbcType=VARCHAR})
UNION
(select <include refid="entityPortfolioColumns"/>
from portfolios p
where p.kee = #{key,jdbcType=VARCHAR})
</select>

<select id="selectEntitiesByKeys" parameterType="string" resultType="Entity">
(select <include refid="entityProjectColumns"/>
from projects p
where p.kee in
<foreach collection="keys" open="(" close=")" item="kee" separator=",">
#{kee,jdbcType=VARCHAR}
</foreach>)
UNION
(select <include refid="entityPortfolioColumns"/>
from portfolios p
where p.kee in
<foreach collection="keys" open="(" close=")" item="kee" separator=",">
#{kee,jdbcType=VARCHAR}
</foreach>) </select>

</mapper>

+ 3
- 59
server/sonar-db-dao/src/main/resources/org/sonar/db/property/PropertiesMapper.xml View File

@@ -3,35 +3,6 @@

<mapper namespace="org.sonar.db.property.PropertiesMapper">

<select id="findUsersForNotification" parameterType="map" resultType="org.sonar.db.property.Subscriber">
SELECT
u.login as "login",
${_true} as "global"
FROM
users u
INNER JOIN properties p ON p.user_uuid = u.uuid
WHERE
p.prop_key = #{notifKey,jdbcType=VARCHAR}
AND p.text_value = 'true'
AND p.component_uuid IS NULL

<if test="projectKey != null">
UNION

SELECT
u.login as "login",
${_false} as "global"
FROM
users u
INNER JOIN components c on c.kee = #{projectKey,jdbcType=VARCHAR}
INNER JOIN properties p ON p.user_uuid = u.uuid
WHERE
p.prop_key = #{notifKey,jdbcType=VARCHAR}
AND p.text_value = 'true'
AND p.component_uuid = c.uuid
</if>
</select>

<select id="findEmailRecipientsForNotification" parameterType="map" resultType="org.sonar.db.EmailSubscriberDto">
SELECT
u.login as "login",
@@ -59,13 +30,13 @@
u.email as "email"
FROM
users u
INNER JOIN components c on
c.kee = #{projectKey,jdbcType=VARCHAR}
INNER JOIN projects proj on
proj.kee = #{projectKey,jdbcType=VARCHAR}
INNER JOIN properties p ON
p.user_uuid = u.uuid
and p.prop_key = #{notifKey,jdbcType=VARCHAR}
and p.text_value = 'true'
and p.component_uuid = c.uuid
and p.component_uuid = proj.uuid
WHERE
u.email is not null
<if test="logins != null">
@@ -189,24 +160,6 @@
</where>
</select>

<select id="selectUuidsByUser" parameterType="map" resultType="String">
select py.uuid
from properties py
where
py.user_uuid=#{userUuid,jdbcType=VARCHAR}
</select>

<select id="selectIdsByMatchingLogin" parameterType="String" resultType="String">
select py.uuid
from properties py
where
py.text_value like #{login,jdbcType=VARCHAR}
and py.prop_key in
<foreach item="property" index="index" collection="propertyKeys" open="(" separator="," close=")">
#{property,jdbcType=VARCHAR}
</foreach>
</select>

<select id="selectByKeyAndMatchingValue" parameterType="map" resultType="ScrapProperty">
select
<include refid="columnsToScrapPropertyDto"/>
@@ -313,15 +266,6 @@
and user_uuid is null
</delete>

<delete id="deleteProjectProperties" parameterType="map">
delete from properties
where
prop_key=#{key}
and text_value = #{value}
and component_uuid is not null
and user_uuid is null
</delete>

<delete id="deleteGlobalProperty" parameterType="string">
delete from properties
where

+ 8
- 7
server/sonar-db-dao/src/testFixtures/java/org/sonar/db/favorite/FavoriteDbTester.java View File

@@ -24,6 +24,7 @@ import org.sonar.db.DbClient;
import org.sonar.db.DbSession;
import org.sonar.db.DbTester;
import org.sonar.db.component.ComponentDto;
import org.sonar.db.entity.EntityDto;
import org.sonar.db.property.PropertyDto;
import org.sonar.db.property.PropertyQuery;

@@ -38,29 +39,29 @@ public class FavoriteDbTester {
this.dbSession = db.getSession();
}

public void add(ComponentDto componentDto, String userUuid, String userLogin) {
public void add(EntityDto entity, String userUuid, String userLogin) {
dbClient.propertiesDao().saveProperty(dbSession, new PropertyDto()
.setKey(PROP_FAVORITE_KEY)
.setUserUuid(userUuid)
.setComponentUuid(componentDto.uuid()),
userLogin, componentDto.getKey(), componentDto.name(), componentDto.qualifier());
.setComponentUuid(entity.getUuid()),
userLogin, entity.getKey(), entity.getName(), entity.getQualifier());
dbSession.commit();
}

public boolean hasFavorite(ComponentDto componentDto, String userUuid) {
public boolean hasFavorite(EntityDto entity, String userUuid) {
List<PropertyDto> result = dbClient.propertiesDao().selectByQuery(PropertyQuery.builder()
.setKey(PROP_FAVORITE_KEY)
.setComponentUuid(componentDto.uuid())
.setComponentUuid(entity.getUuid())
.setUserUuid(userUuid)
.build(), dbSession);

return !result.isEmpty();
}

public boolean hasNoFavorite(ComponentDto componentDto) {
public boolean hasNoFavorite(EntityDto entity) {
List<PropertyDto> result = dbClient.propertiesDao().selectByQuery(PropertyQuery.builder()
.setKey(PROP_FAVORITE_KEY)
.setComponentUuid(componentDto.uuid())
.setComponentUuid(entity.getUuid())
.build(), dbSession);
return result.isEmpty();
}

+ 9
- 9
server/sonar-db-dao/src/testFixtures/java/org/sonar/db/property/PropertyDbTester.java View File

@@ -28,7 +28,7 @@ import javax.annotation.Nullable;
import org.sonar.db.DbClient;
import org.sonar.db.DbSession;
import org.sonar.db.DbTester;
import org.sonar.db.component.ComponentDto;
import org.sonar.db.entity.EntityDto;

import static java.util.Arrays.asList;
import static java.util.Collections.singletonList;
@@ -75,15 +75,15 @@ public class PropertyDbTester {
null, null, null, null);
}

public void insertPropertySet(String settingBaseKey, @Nullable ComponentDto componentDto, Map<String, String>... fieldValues) {
public void insertPropertySet(String settingBaseKey, @Nullable EntityDto entity, Map<String, String>... fieldValues) {
int index = 1;
List<PropertyDto> propertyDtos = new ArrayList<>();
List<String> ids = new ArrayList<>();
for (Map<String, String> fieldValue : fieldValues) {
for (Map.Entry<String, String> entry : fieldValue.entrySet()) {
String key = settingBaseKey + "." + index + "." + entry.getKey();
if (componentDto != null) {
propertyDtos.add(newComponentPropertyDto(componentDto).setKey(key).setValue(entry.getValue()));
if (entity != null) {
propertyDtos.add(newComponentPropertyDto(entity).setKey(key).setValue(entry.getValue()));
} else {
propertyDtos.add(newGlobalPropertyDto().setKey(key).setValue(entry.getValue()));
}
@@ -92,14 +92,14 @@ public class PropertyDbTester {
index++;
}
String idsValue = Joiner.on(",").join(ids);
if (componentDto != null) {
propertyDtos.add(newComponentPropertyDto(componentDto).setKey(settingBaseKey).setValue(idsValue));
if (entity != null) {
propertyDtos.add(newComponentPropertyDto(entity).setKey(settingBaseKey).setValue(idsValue));
} else {
propertyDtos.add(newGlobalPropertyDto().setKey(settingBaseKey).setValue(idsValue));
}
String componentKey = componentDto == null ? null : componentDto.getKey();
String componentName = componentDto == null ? null : componentDto.name();
String qualififer = componentDto == null ? null : componentDto.qualifier();
String componentKey = entity == null ? null : entity.getKey();
String componentName = entity == null ? null : entity.getName();
String qualififer = entity == null ? null : entity.getQualifier();
insertProperties(propertyDtos, null, componentKey, componentName, qualififer);
}


+ 4
- 3
server/sonar-db-dao/src/testFixtures/java/org/sonar/db/property/PropertyTesting.java View File

@@ -22,6 +22,7 @@ package org.sonar.db.property;
import javax.annotation.Nullable;
import org.apache.commons.lang.math.RandomUtils;
import org.sonar.db.component.ComponentDto;
import org.sonar.db.entity.EntityDto;
import org.sonar.db.user.UserDto;

import static com.google.common.base.Preconditions.checkNotNull;
@@ -47,9 +48,9 @@ public class PropertyTesting {
return newPropertyDto(key, value, component.uuid(), null);
}

public static PropertyDto newComponentPropertyDto(ComponentDto component) {
checkNotNull(component.uuid());
return newPropertyDto(component.uuid(), null);
public static PropertyDto newComponentPropertyDto(EntityDto entity) {
checkNotNull(entity.getUuid());
return newPropertyDto(entity.getUuid(), null);
}

public static PropertyDto newUserPropertyDto(String key, String value, UserDto user) {

+ 17
- 15
server/sonar-server-common/src/it/java/org/sonar/server/favorite/FavoriteUpdaterIT.java View File

@@ -26,6 +26,8 @@ import org.sonar.db.DbClient;
import org.sonar.db.DbSession;
import org.sonar.db.DbTester;
import org.sonar.db.component.ComponentDto;
import org.sonar.db.entity.EntityDto;
import org.sonar.db.project.ProjectDto;
import org.sonar.db.property.PropertyQuery;
import org.sonar.db.user.UserDto;

@@ -44,7 +46,7 @@ public class FavoriteUpdaterIT {

@Test
public void put_favorite() {
ComponentDto project = db.components().insertPrivateProject().getMainBranchComponent();
ProjectDto project = db.components().insertPrivateProject().getProjectDto();
UserDto user = db.users().insertUser();
assertNoFavorite(project, user);

@@ -55,23 +57,23 @@ public class FavoriteUpdaterIT {

@Test
public void do_nothing_when_no_user() {
ComponentDto project = db.components().insertPrivateProject().getMainBranchComponent();
ProjectDto project = db.components().insertPrivateProject().getProjectDto();

underTest.add(dbSession, project, null, null,true);

assertThat(dbClient.propertiesDao().selectByQuery(PropertyQuery.builder()
.setComponentUuid(project.uuid())
.setComponentUuid(project.getUuid())
.build(), dbSession)).isEmpty();
}

@Test
public void do_not_add_favorite_when_already_100_favorite_projects() {
UserDto user = db.users().insertUser();
IntStream.rangeClosed(1, 100).forEach(i -> db.favorites().add(db.components().insertPrivateProject().getMainBranchComponent(), user.getUuid(), user.getName()));
IntStream.rangeClosed(1, 100).forEach(i -> db.favorites().add(db.components().insertPrivateProject().getProjectDto(), user.getUuid(), user.getName()));
assertThat(dbClient.propertiesDao().selectByQuery(PropertyQuery.builder()
.setUserUuid(user.getUuid())
.build(), dbSession)).hasSize(100);
ComponentDto project = db.components().insertPrivateProject().getMainBranchComponent();
ProjectDto project = db.components().insertPrivateProject().getProjectDto();

underTest.add(dbSession, project, user.getUuid(), user.getLogin(), false);

@@ -83,12 +85,12 @@ public class FavoriteUpdaterIT {
@Test
public void do_not_add_favorite_when_already_100_favorite_portfolios() {
UserDto user = db.users().insertUser();
IntStream.rangeClosed(1, 100).forEach(i -> db.favorites().add(db.components().insertPrivateProject().getMainBranchComponent(),
IntStream.rangeClosed(1, 100).forEach(i -> db.favorites().add(db.components().insertPrivateProject().getProjectDto(),
user.getUuid(), user.getLogin()));
assertThat(dbClient.propertiesDao().selectByQuery(PropertyQuery.builder()
.setUserUuid(user.getUuid())
.build(), dbSession)).hasSize(100);
ComponentDto project = db.components().insertPrivateProject().getMainBranchComponent();
ProjectDto project = db.components().insertPrivateProject().getProjectDto();

underTest.add(dbSession, project, user.getUuid(), user.getLogin(), false);

@@ -100,9 +102,9 @@ public class FavoriteUpdaterIT {
@Test
public void fail_when_more_than_100_projects_favorites() {
UserDto user = db.users().insertUser();
IntStream.rangeClosed(1, 100).forEach(i -> db.favorites().add(db.components().insertPrivateProject().getMainBranchComponent(),
IntStream.rangeClosed(1, 100).forEach(i -> db.favorites().add(db.components().insertPrivateProject().getProjectDto(),
user.getUuid(), user.getLogin()));
ComponentDto project = db.components().insertPrivateProject().getMainBranchComponent();
ProjectDto project = db.components().insertPrivateProject().getProjectDto();

assertThatThrownBy(() -> underTest.add(dbSession, project, user.getUuid(), user.getLogin(), true))
.isInstanceOf(IllegalArgumentException.class)
@@ -111,27 +113,27 @@ public class FavoriteUpdaterIT {

@Test
public void fail_when_adding_existing_favorite() {
ComponentDto project = db.components().insertPrivateProject().getMainBranchComponent();
ProjectDto project = db.components().insertPrivateProject().getProjectDto();
UserDto user = db.users().insertUser();
underTest.add(dbSession, project, user.getUuid(), user.getLogin(), true);
assertFavorite(project, user);

assertThatThrownBy(() -> underTest.add(dbSession, project, user.getUuid(), user.getLogin(), true))
.isInstanceOf(IllegalArgumentException.class)
.hasMessage(String.format("Component '%s' (uuid: %s) is already a favorite", project.getKey(), project.uuid()));
.hasMessage(String.format("Component '%s' (uuid: %s) is already a favorite", project.getKey(), project.getUuid()));
}

private void assertFavorite(ComponentDto project, UserDto user) {
private void assertFavorite(EntityDto entity, UserDto user) {
assertThat(dbClient.propertiesDao().selectByQuery(PropertyQuery.builder()
.setUserUuid(user.getUuid())
.setComponentUuid(project.uuid())
.setComponentUuid(entity.getUuid())
.build(), dbSession)).hasSize(1);
}

private void assertNoFavorite(ComponentDto project, UserDto user) {
private void assertNoFavorite(EntityDto entity, UserDto user) {
assertThat(dbClient.propertiesDao().selectByQuery(PropertyQuery.builder()
.setUserUuid(user.getUuid())
.setComponentUuid(project.uuid())
.setComponentUuid(entity.getUuid())
.build(), dbSession)).isEmpty();
}
}

+ 8
- 7
server/sonar-server-common/src/main/java/org/sonar/server/favorite/FavoriteUpdater.java View File

@@ -24,6 +24,7 @@ import javax.annotation.Nullable;
import org.sonar.db.DbClient;
import org.sonar.db.DbSession;
import org.sonar.db.component.ComponentDto;
import org.sonar.db.entity.EntityDto;
import org.sonar.db.property.PropertyDto;
import org.sonar.db.property.PropertyQuery;

@@ -41,7 +42,7 @@ public class FavoriteUpdater {
/**
* Set favorite to the logged in user. If no user, no action is done
*/
public void add(DbSession dbSession, ComponentDto componentDto, @Nullable String userUuid, @Nullable String userLogin, boolean failIfTooManyFavorites) {
public void add(DbSession dbSession, EntityDto entity, @Nullable String userUuid, @Nullable String userLogin, boolean failIfTooManyFavorites) {
if (userUuid == null) {
return;
}
@@ -49,21 +50,21 @@ public class FavoriteUpdater {
List<PropertyDto> existingFavoriteOnComponent = dbClient.propertiesDao().selectByQuery(PropertyQuery.builder()
.setKey(PROP_FAVORITE_KEY)
.setUserUuid(userUuid)
.setComponentUuid(componentDto.uuid())
.setComponentUuid(entity.getUuid())
.build(), dbSession);
checkArgument(existingFavoriteOnComponent.isEmpty(), "Component '%s' (uuid: %s) is already a favorite", componentDto.getKey(), componentDto.uuid());
checkArgument(existingFavoriteOnComponent.isEmpty(), "Component '%s' (uuid: %s) is already a favorite", entity.getKey(), entity.getUuid());

List<PropertyDto> existingFavorites = dbClient.propertiesDao().selectByKeyAndUserUuidAndComponentQualifier(dbSession, PROP_FAVORITE_KEY, userUuid, componentDto.qualifier());
List<PropertyDto> existingFavorites = dbClient.propertiesDao().selectByKeyAndUserUuidAndComponentQualifier(dbSession, PROP_FAVORITE_KEY, userUuid, entity.getQualifier());
if (existingFavorites.size() >= 100) {
checkArgument(!failIfTooManyFavorites, "You cannot have more than 100 favorites on components with qualifier '%s'", componentDto.qualifier());
checkArgument(!failIfTooManyFavorites, "You cannot have more than 100 favorites on components with qualifier '%s'", entity.getQualifier());
return;
}
dbClient.propertiesDao().saveProperty(dbSession, new PropertyDto()
.setKey(PROP_FAVORITE_KEY)
.setComponentUuid(componentDto.uuid())
.setComponentUuid(entity.getUuid())
.setUserUuid(userUuid),
userLogin,
componentDto.getKey(), componentDto.name(), componentDto.qualifier());
entity.getKey(), entity.getName(), entity.getQualifier());
}

/**

+ 0
- 76
server/sonar-server-common/src/main/java/org/sonar/server/notification/DefaultNotificationManager.java View File

@@ -113,69 +113,10 @@ public class DefaultNotificationManager implements NotificationManager {
return dbClient.notificationQueueDao().count();
}

/**
* {@inheritDoc}
*/
@Override
public Multimap<String, NotificationChannel> findSubscribedRecipientsForDispatcher(NotificationDispatcher dispatcher,
String projectKey, SubscriberPermissionsOnProject subscriberPermissionsOnProject) {
verifyProjectKey(projectKey);
String dispatcherKey = dispatcher.getKey();

Set<SubscriberAndChannel> subscriberAndChannels = Arrays.stream(notificationChannels)
.flatMap(notificationChannel -> toSubscriberAndChannels(dispatcherKey, projectKey, notificationChannel))
.collect(Collectors.toSet());

if (subscriberAndChannels.isEmpty()) {
return ImmutableMultimap.of();
}

ImmutableSetMultimap.Builder<String, NotificationChannel> builder = ImmutableSetMultimap.builder();
try (DbSession dbSession = dbClient.openSession(false)) {
Set<String> authorizedLogins = keepAuthorizedLogins(dbSession, projectKey, subscriberAndChannels, subscriberPermissionsOnProject);
subscriberAndChannels.stream()
.filter(subscriberAndChannel -> authorizedLogins.contains(subscriberAndChannel.subscriber().getLogin()))
.forEach(subscriberAndChannel -> builder.put(subscriberAndChannel.subscriber().getLogin(), subscriberAndChannel.channel()));
}
return builder.build();
}

private static void verifyProjectKey(String projectKey) {
requireNonNull(projectKey, "projectKey is mandatory");
}

private Stream<SubscriberAndChannel> toSubscriberAndChannels(String dispatcherKey, String projectKey, NotificationChannel notificationChannel) {
Set<Subscriber> usersForNotification = dbClient.propertiesDao().findUsersForNotification(dispatcherKey, notificationChannel.getKey(), projectKey);
return usersForNotification
.stream()
.map(login -> new SubscriberAndChannel(login, notificationChannel));
}

private Set<String> keepAuthorizedLogins(DbSession dbSession, String projectKey, Set<SubscriberAndChannel> subscriberAndChannels,
SubscriberPermissionsOnProject requiredPermissions) {
if (requiredPermissions.getGlobalSubscribers().equals(requiredPermissions.getProjectSubscribers())) {
return keepAuthorizedLogins(dbSession, projectKey, subscriberAndChannels, null, requiredPermissions.getGlobalSubscribers());
} else {
return Stream
.concat(
keepAuthorizedLogins(dbSession, projectKey, subscriberAndChannels, true, requiredPermissions.getGlobalSubscribers()).stream(),
keepAuthorizedLogins(dbSession, projectKey, subscriberAndChannels, false, requiredPermissions.getProjectSubscribers()).stream())
.collect(Collectors.toSet());
}
}

private Set<String> keepAuthorizedLogins(DbSession dbSession, String projectKey, Set<SubscriberAndChannel> subscriberAndChannels,
@Nullable Boolean global, String permission) {
Set<String> logins = subscriberAndChannels.stream()
.filter(s -> global == null || s.subscriber().isGlobal() == global)
.map(s -> s.subscriber().getLogin())
.collect(Collectors.toSet());
if (logins.isEmpty()) {
return Collections.emptySet();
}
return dbClient.authorizationDao().keepAuthorizedLoginsOnProject(dbSession, logins, projectKey, permission);
}

@Override
public Set<EmailRecipient> findSubscribedEmailRecipients(String dispatcherKey, String projectKey, SubscriberPermissionsOnProject subscriberPermissionsOnProject) {
verifyProjectKey(projectKey);
@@ -244,23 +185,6 @@ public class DefaultNotificationManager implements NotificationManager {
.filter(s -> authorizedLogins.contains(s.getLogin()));
}

private record SubscriberAndChannel(Subscriber subscriber, NotificationChannel channel) {

@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
SubscriberAndChannel that = (SubscriberAndChannel) o;
return Objects.equals(subscriber, that.subscriber) &&
Objects.equals(channel, that.channel);
}

}

@VisibleForTesting
protected List<NotificationChannel> getChannels() {
return Arrays.asList(notificationChannels);

+ 0
- 17
server/sonar-server-common/src/main/java/org/sonar/server/notification/NotificationManager.java View File

@@ -44,23 +44,6 @@ public interface NotificationManager {
*/
<T extends Notification> void scheduleForSending(T notification);

/**
* <p>
* Returns the list of users who subscribed to the given dispatcher, along with the notification channels (email, twitter, ...) that they choose
* for this dispatcher.
* </p>
* <p>
* The resource ID can be null in case of notifications that have nothing to do with a specific project (like system notifications).
* </p>
*
* @param dispatcher the dispatcher for which this list of users is requested
* @param projectKey key of the project
* @param subscriberPermissionsOnProject the required permission for global and project subscribers
* @return the list of user login along with the subscribed channels
*/
Multimap<String, NotificationChannel> findSubscribedRecipientsForDispatcher(NotificationDispatcher dispatcher, String projectKey,
SubscriberPermissionsOnProject subscriberPermissionsOnProject);

record EmailRecipient(String login, String email) {
public EmailRecipient(String login, String email) {
this.login = requireNonNull(login, "login can't be null");

+ 0
- 119
server/sonar-server-common/src/test/java/org/sonar/server/notification/DefaultNotificationManagerTest.java View File

@@ -142,125 +142,6 @@ public class DefaultNotificationManagerTest {
verify(underTest, times(1)).logDeserializationIssue();
}

@Test
public void shouldFindNoRecipient() {
assertThat(underTest.findSubscribedRecipientsForDispatcher(dispatcher, "uuid_45", new SubscriberPermissionsOnProject(UserRole.USER)).asMap().entrySet())
.isEmpty();
}

@Test
public void shouldFindSubscribedRecipientForGivenResource() {
String projectKey = randomAlphabetic(6);
String otherProjectKey = randomAlphabetic(7);
when(propertiesDao.findUsersForNotification("NewViolations", "Email", projectKey))
.thenReturn(newHashSet(new Subscriber("user1", false), new Subscriber("user3", false), new Subscriber("user3", true)));
when(propertiesDao.findUsersForNotification("NewViolations", "Twitter", otherProjectKey))
.thenReturn(newHashSet(new Subscriber("user2", false)));
when(propertiesDao.findUsersForNotification("NewViolations", "Twitter", projectKey))
.thenReturn(newHashSet(new Subscriber("user3", true)));
when(propertiesDao.findUsersForNotification("NewAlerts", "Twitter", projectKey))
.thenReturn(newHashSet(new Subscriber("user4", false)));

when(authorizationDao.keepAuthorizedLoginsOnProject(dbSession, newHashSet("user1", "user3"), projectKey, "user"))
.thenReturn(newHashSet("user1", "user3"));

Multimap<String, NotificationChannel> multiMap = underTest.findSubscribedRecipientsForDispatcher(dispatcher, projectKey,
ALL_MUST_HAVE_ROLE_USER);
assertThat(multiMap.entries()).hasSize(3);

Map<String, Collection<NotificationChannel>> map = multiMap.asMap();
assertThat(map.get("user1")).containsOnly(emailChannel);
assertThat(map.get("user2")).isNull();
assertThat(map.get("user3")).containsOnly(emailChannel, twitterChannel);
assertThat(map.get("user4")).isNull();

// code is optimized to perform only 1 SQL requests for all channels
verify(authorizationDao, times(1)).keepAuthorizedLoginsOnProject(eq(dbSession), anySet(), anyString(), anyString());
}

@Test
public void should_apply_distinct_permission_filtering_global_or_project_subscribers() {
String globalPermission = randomAlphanumeric(4);
String projectPermission = randomAlphanumeric(5);
String projectKey = randomAlphabetic(6);
String otherProjectKey = randomAlphabetic(7);
when(propertiesDao.findUsersForNotification("NewViolations", "Email", projectKey))
.thenReturn(newHashSet(new Subscriber("user1", false), new Subscriber("user3", false), new Subscriber("user3", true)));
when(propertiesDao.findUsersForNotification("NewViolations", "Twitter", otherProjectKey))
.thenReturn(newHashSet(new Subscriber("user2", false)));
when(propertiesDao.findUsersForNotification("NewViolations", "Twitter", projectKey))
.thenReturn(newHashSet(new Subscriber("user3", true)));
when(propertiesDao.findUsersForNotification("NewAlerts", "Twitter", projectKey))
.thenReturn(newHashSet(new Subscriber("user4", false)));

when(authorizationDao.keepAuthorizedLoginsOnProject(dbSession, newHashSet("user3", "user4"), projectKey, globalPermission))
.thenReturn(newHashSet("user3"));
when(authorizationDao.keepAuthorizedLoginsOnProject(dbSession, newHashSet("user1", "user3"), projectKey, projectPermission))
.thenReturn(newHashSet("user1", "user3"));

Multimap<String, NotificationChannel> multiMap = underTest.findSubscribedRecipientsForDispatcher(dispatcher, projectKey,
new SubscriberPermissionsOnProject(globalPermission, projectPermission));
assertThat(multiMap.entries()).hasSize(3);

Map<String, Collection<NotificationChannel>> map = multiMap.asMap();
assertThat(map.get("user1")).containsOnly(emailChannel);
assertThat(map.get("user2")).isNull();
assertThat(map.get("user3")).containsOnly(emailChannel, twitterChannel);
assertThat(map.get("user4")).isNull();

// code is optimized to perform only 2 SQL requests for all channels
verify(authorizationDao, times(1)).keepAuthorizedLoginsOnProject(eq(dbSession), anySet(), anyString(), eq(globalPermission));
verify(authorizationDao, times(1)).keepAuthorizedLoginsOnProject(eq(dbSession), anySet(), anyString(), eq(projectPermission));
}

@Test
public void do_not_call_db_for_project_permission_filtering_if_there_is_no_project_subscriber() {
String globalPermission = randomAlphanumeric(4);
String projectPermission = randomAlphanumeric(5);
String projectKey = randomAlphabetic(6);
when(propertiesDao.findUsersForNotification("NewViolations", "Email", projectKey))
.thenReturn(newHashSet(new Subscriber("user3", true)));
when(propertiesDao.findUsersForNotification("NewViolations", "Twitter", projectKey))
.thenReturn(newHashSet(new Subscriber("user3", true)));

when(authorizationDao.keepAuthorizedLoginsOnProject(dbSession, newHashSet("user3"), projectKey, globalPermission))
.thenReturn(newHashSet("user3"));

Multimap<String, NotificationChannel> multiMap = underTest.findSubscribedRecipientsForDispatcher(dispatcher, projectKey,
new SubscriberPermissionsOnProject(globalPermission, projectPermission));
assertThat(multiMap.entries()).hasSize(2);

Map<String, Collection<NotificationChannel>> map = multiMap.asMap();
assertThat(map.get("user3")).containsOnly(emailChannel, twitterChannel);

verify(authorizationDao, times(1)).keepAuthorizedLoginsOnProject(eq(dbSession), anySet(), anyString(), eq(globalPermission));
verify(authorizationDao, times(0)).keepAuthorizedLoginsOnProject(eq(dbSession), anySet(), anyString(), eq(projectPermission));
}

@Test
public void do_not_call_db_for_project_permission_filtering_if_there_is_no_global_subscriber() {
String globalPermission = randomAlphanumeric(4);
String projectPermission = randomAlphanumeric(5);
String projectKey = randomAlphabetic(6);
when(propertiesDao.findUsersForNotification("NewViolations", "Email", projectKey))
.thenReturn(newHashSet(new Subscriber("user3", false)));
when(propertiesDao.findUsersForNotification("NewViolations", "Twitter", projectKey))
.thenReturn(newHashSet(new Subscriber("user3", false)));

when(authorizationDao.keepAuthorizedLoginsOnProject(dbSession, newHashSet("user3"), projectKey, projectPermission))
.thenReturn(newHashSet("user3"));

Multimap<String, NotificationChannel> multiMap = underTest.findSubscribedRecipientsForDispatcher(dispatcher, projectKey,
new SubscriberPermissionsOnProject(globalPermission, projectPermission));
assertThat(multiMap.entries()).hasSize(2);

Map<String, Collection<NotificationChannel>> map = multiMap.asMap();
assertThat(map.get("user3")).containsOnly(emailChannel, twitterChannel);

verify(authorizationDao, times(0)).keepAuthorizedLoginsOnProject(eq(dbSession), anySet(), anyString(), eq(globalPermission));
verify(authorizationDao, times(1)).keepAuthorizedLoginsOnProject(eq(dbSession), anySet(), anyString(), eq(projectPermission));
}

@Test
public void findSubscribedEmailRecipients_fails_with_NPE_if_projectKey_is_null() {
String dispatcherKey = randomAlphabetic(12);

+ 30
- 1
server/sonar-webserver-auth/src/main/java/org/sonar/server/user/AbstractUserSession.java View File

@@ -28,6 +28,7 @@ import javax.annotation.Nullable;
import org.sonar.api.web.UserRole;
import org.sonar.core.util.stream.MoreCollectors;
import org.sonar.db.component.ComponentDto;
import org.sonar.db.entity.EntityDto;
import org.sonar.db.permission.GlobalPermission;
import org.sonar.db.project.ProjectDto;
import org.sonar.db.user.UserDto;
@@ -88,7 +89,6 @@ public abstract class AbstractUserSession implements UserSession {

@Override
public boolean hasComponentPermission(String permission, ComponentDto component) {

Optional<String> projectUuid1 = componentUuidToProjectUuid(component.uuid());

return projectUuid1
@@ -101,6 +101,11 @@ public abstract class AbstractUserSession implements UserSession {
return hasProjectUuidPermission(permission, project.getUuid());
}

@Override
public final boolean hasEntityPermission(String permission, EntityDto entity) {
return hasProjectUuidPermission(permission, entity.getUuid());
}

@Override
public final boolean hasProjectPermission(String permission, String projectUuid) {
return hasProjectUuidPermission(permission, projectUuid);
@@ -148,6 +153,21 @@ public abstract class AbstractUserSession implements UserSession {
return doKeepAuthorizedProjects(permission, projects);
}

@Override
public <T extends EntityDto> List<T> keepAuthorizedEntities(String permission, Collection<T> projects) {
return doKeepAuthorizedEntities(permission, projects);
}

/**
* Naive implementation, to be overridden if needed
*/
protected <T extends EntityDto> List<T> doKeepAuthorizedEntities(String permission, Collection<T> entities) {
boolean allowPublicComponent = PUBLIC_PERMISSIONS.contains(permission);
return entities.stream()
.filter(c -> (allowPublicComponent && !c.isPrivate()) || hasProjectPermission(permission, c.getUuid()))
.toList();
}

/**
* Naive implementation, to be overridden if needed
*/
@@ -201,6 +221,15 @@ public abstract class AbstractUserSession implements UserSession {
throw new ForbiddenException(INSUFFICIENT_PRIVILEGES_MESSAGE);
}

@Override
public UserSession checkEntityPermission(String projectPermission, EntityDto entity) {
if (hasEntityPermission(projectPermission, entity)) {
return this;
}

throw new ForbiddenException(INSUFFICIENT_PRIVILEGES_MESSAGE);
}

@Override
public UserSession checkChildProjectsPermission(String projectPermission, ComponentDto component) {
if (!APP.equals(component.qualifier()) || hasChildProjectsPermission(projectPermission, component)) {

+ 12
- 0
server/sonar-webserver-auth/src/main/java/org/sonar/server/user/ServerUserSession.java View File

@@ -41,6 +41,7 @@ import org.sonar.db.component.BranchDto;
import org.sonar.db.component.ComponentDto;
import org.sonar.db.component.ComponentTreeQuery;
import org.sonar.db.component.ComponentTreeQuery.Strategy;
import org.sonar.db.entity.EntityDto;
import org.sonar.db.permission.GlobalPermission;
import org.sonar.db.project.ProjectDto;
import org.sonar.db.user.GroupDto;
@@ -195,6 +196,17 @@ public class ServerUserSession extends AbstractUserSession {
.toList();
}

@Override
public <T extends EntityDto> List<T> keepAuthorizedEntities(String permission, Collection<T> projects) {
Set<String> projectsUuids = projects.stream().map(EntityDto::getUuid).collect(Collectors.toSet());
// TODO
Set<String> authorizedProjectsUuids = keepProjectsUuidsByPermission(permission, projectsUuids);

return projects.stream()
.filter(project -> authorizedProjectsUuids.contains(project.getUuid()))
.toList();
}

private Set<String> keepProjectsUuidsByPermission(String permission, Collection<String> projectsUuids) {
try (DbSession dbSession = dbClient.openSession(false)) {
String userUuid = userDto == null ? null : userDto.getUuid();

+ 17
- 1
server/sonar-webserver-auth/src/main/java/org/sonar/server/user/ThreadLocalUserSession.java View File

@@ -24,6 +24,7 @@ import java.util.List;
import java.util.Optional;
import javax.annotation.CheckForNull;
import org.sonar.db.component.ComponentDto;
import org.sonar.db.entity.EntityDto;
import org.sonar.db.permission.GlobalPermission;
import org.sonar.db.project.ProjectDto;
import org.sonar.db.user.GroupDto;
@@ -128,6 +129,12 @@ public class ThreadLocalUserSession implements UserSession {
return this;
}

@Override
public UserSession checkEntityPermission(String projectPermission, EntityDto entity) {
get().checkEntityPermission(projectPermission, entity);
return this;
}

@Override
public UserSession checkProjectPermission(String projectPermission, ProjectDto project) {
get().checkProjectPermission(projectPermission, project);
@@ -173,6 +180,11 @@ public class ThreadLocalUserSession implements UserSession {
return get().hasComponentPermission(permission, component);
}

@Override
public boolean hasEntityPermission(String permission, EntityDto entity) {
return get().hasEntityPermission(permission, entity);
}

@Override
public boolean hasProjectPermission(String permission, ProjectDto project) {
return get().hasProjectPermission(permission, project);
@@ -208,9 +220,13 @@ public class ThreadLocalUserSession implements UserSession {
return get().keepAuthorizedComponents(permission, components);
}

@Override
public <T extends EntityDto> List<T> keepAuthorizedEntities(String permission, Collection<T> entities) {
return get().keepAuthorizedEntities(permission, entities);
}

@Override
public List<ProjectDto> keepAuthorizedProjects(String permission, Collection<ProjectDto> projects) {
return get().keepAuthorizedProjects(permission, projects);
}

}

+ 8
- 0
server/sonar-webserver-auth/src/main/java/org/sonar/server/user/UserSession.java View File

@@ -25,7 +25,9 @@ import java.util.List;
import java.util.Optional;
import javax.annotation.CheckForNull;
import org.sonar.db.component.ComponentDto;
import org.sonar.db.entity.EntityDto;
import org.sonar.db.permission.GlobalPermission;
import org.sonar.db.portfolio.PortfolioDto;
import org.sonar.db.project.ProjectDto;
import org.sonar.db.user.GroupDto;

@@ -149,6 +151,8 @@ public interface UserSession {

boolean hasProjectPermission(String permission, ProjectDto project);

boolean hasEntityPermission(String permission, EntityDto entity);

boolean hasProjectPermission(String permission, String projectUuid);

boolean hasChildProjectsPermission(String permission, ComponentDto component);
@@ -176,6 +180,8 @@ public interface UserSession {
*/
List<ComponentDto> keepAuthorizedComponents(String permission, Collection<ComponentDto> components);

<T extends EntityDto> List<T> keepAuthorizedEntities(String permission, Collection<T> components);

List<ProjectDto> keepAuthorizedProjects(String permission, Collection<ProjectDto> projects);

/**
@@ -190,6 +196,8 @@ public interface UserSession {
*/
UserSession checkProjectPermission(String projectPermission, ProjectDto project);

UserSession checkEntityPermission(String projectPermission, EntityDto entity);

/**
* Ensures that {@link #hasChildProjectsPermission(String, ComponentDto)} is {@code true}
* otherwise throws a {@link org.sonar.server.exceptions.ForbiddenException}.

+ 18
- 1
server/sonar-webserver-auth/src/testFixtures/java/org/sonar/server/tester/UserSessionRule.java View File

@@ -30,6 +30,7 @@ import org.junit.runner.Description;
import org.junit.runners.model.Statement;
import org.sonar.db.component.BranchDto;
import org.sonar.db.component.ComponentDto;
import org.sonar.db.entity.EntityDto;
import org.sonar.db.permission.GlobalPermission;
import org.sonar.db.portfolio.PortfolioDto;
import org.sonar.db.project.ProjectDto;
@@ -204,7 +205,7 @@ public class UserSessionRule implements TestRule, UserSession {
return this;
}

public UserSession registerBranches(BranchDto ...branchDtos){
public UserSession registerBranches(BranchDto... branchDtos) {
ensureAbstractMockUserSession().registerBranches(branchDtos);
return this;
}
@@ -262,6 +263,11 @@ public class UserSessionRule implements TestRule, UserSession {
return currentUserSession.hasProjectPermission(permission, project);
}

@Override
public boolean hasEntityPermission(String permission, EntityDto entity) {
return currentUserSession.hasProjectPermission(permission, entity.getUuid());
}

@Override
public boolean hasProjectPermission(String permission, String projectUuid) {
return currentUserSession.hasProjectPermission(permission, projectUuid);
@@ -292,6 +298,11 @@ public class UserSessionRule implements TestRule, UserSession {
return currentUserSession.keepAuthorizedComponents(permission, components);
}

@Override
public <T extends EntityDto> List<T> keepAuthorizedEntities(String permission, Collection<T> entities) {
return currentUserSession.keepAuthorizedEntities(permission, entities);
}

@Override
public List<ProjectDto> keepAuthorizedProjects(String permission, Collection<ProjectDto> projects) {
return currentUserSession.keepAuthorizedProjects(permission, projects);
@@ -369,6 +380,12 @@ public class UserSessionRule implements TestRule, UserSession {
return this;
}

@Override
public UserSession checkEntityPermission(String projectPermission, EntityDto entity) {
currentUserSession.checkEntityPermission(projectPermission, entity);
return this;
}

@Override
public UserSession checkProjectPermission(String projectPermission, ProjectDto project) {
currentUserSession.checkProjectPermission(projectPermission, project);

+ 13
- 15
server/sonar-webserver-webapi/src/it/java/org/sonar/server/ce/queue/ReportSubmitterIT.java View File

@@ -40,6 +40,7 @@ import org.sonar.db.DbTester;
import org.sonar.db.ce.CeTaskTypes;
import org.sonar.db.component.ComponentDto;
import org.sonar.db.permission.GlobalPermission;
import org.sonar.db.project.ProjectDto;
import org.sonar.db.user.UserDto;
import org.sonar.server.component.ComponentUpdater;
import org.sonar.server.es.TestProjectIndexers;
@@ -91,11 +92,8 @@ public class ReportSubmitterIT {
private final TestProjectIndexers projectIndexers = new TestProjectIndexers();
private final PermissionTemplateService permissionTemplateService = mock(PermissionTemplateService.class);

private final Configuration config = mock(Configuration.class);

private final ComponentUpdater componentUpdater = new ComponentUpdater(db.getDbClient(), mock(I18n.class), mock(System2.class), permissionTemplateService,
new FavoriteUpdater(db.getDbClient()), projectIndexers, new SequenceUuidFactory(), defaultBranchNameResolver, true
);
new FavoriteUpdater(db.getDbClient()), projectIndexers, new SequenceUuidFactory(), defaultBranchNameResolver, true);
private final BranchSupport ossEditionBranchSupport = new BranchSupport(null);

private final ReportSubmitter underTest = new ReportSubmitter(queue, userSession, componentUpdater, permissionTemplateService, db.getDbClient(), ossEditionBranchSupport,
@@ -146,7 +144,7 @@ public class ReportSubmitterIT {
userSession.logIn(user).addProjectPermission(SCAN.getKey(), project);
mockSuccessfulPrepareSubmitCall();

underTest.submit(project.getKey(), project.name(), emptyMap(), IOUtils.toInputStream("{binary}", StandardCharsets.UTF_8));
underTest.submit(project.getKey(), project.name(), emptyMap(), IOUtils.toInputStream("{binary}", UTF_8));

verifyReportIsPersisted(TASK_UUID);
verifyNoInteractions(permissionTemplateService);
@@ -165,7 +163,7 @@ public class ReportSubmitterIT {
when(permissionTemplateService.wouldUserHaveScanPermissionWithDefaultTemplate(any(DbSession.class), any(), eq(PROJECT_KEY))).thenReturn(true);
when(permissionTemplateService.hasDefaultTemplateWithPermissionOnProjectCreator(any(DbSession.class), any(ComponentDto.class))).thenReturn(true);

underTest.submit(PROJECT_KEY, PROJECT_NAME, emptyMap(), IOUtils.toInputStream("{binary}"));
underTest.submit(PROJECT_KEY, PROJECT_NAME, emptyMap(), IOUtils.toInputStream("{binary}", UTF_8));

ComponentDto createdProject = db.getDbClient().componentDao().selectByKey(db.getSession(), PROJECT_KEY).get();
verifyReportIsPersisted(TASK_UUID);
@@ -185,9 +183,9 @@ public class ReportSubmitterIT {
when(permissionTemplateService.wouldUserHaveScanPermissionWithDefaultTemplate(any(DbSession.class), any(), eq(PROJECT_KEY))).thenReturn(true);
when(permissionTemplateService.hasDefaultTemplateWithPermissionOnProjectCreator(any(DbSession.class), any(ComponentDto.class))).thenReturn(true);

underTest.submit(PROJECT_KEY, PROJECT_NAME, emptyMap(), IOUtils.toInputStream("{binary}"));
underTest.submit(PROJECT_KEY, PROJECT_NAME, emptyMap(), IOUtils.toInputStream("{binary}", UTF_8));

ComponentDto createdProject = db.getDbClient().componentDao().selectByKey(db.getSession(), PROJECT_KEY).get();
ProjectDto createdProject = db.getDbClient().projectDao().selectProjectByKey(db.getSession(), PROJECT_KEY).get();
assertThat(db.favorites().hasFavorite(createdProject, user.getUuid())).isTrue();
}

@@ -203,14 +201,14 @@ public class ReportSubmitterIT {

underTest.submit(PROJECT_KEY, PROJECT_NAME, emptyMap(), IOUtils.toInputStream("{binary}"));

ComponentDto createdProject = db.getDbClient().componentDao().selectByKey(db.getSession(), PROJECT_KEY).get();
ProjectDto createdProject = db.getDbClient().projectDao().selectProjectByKey(db.getSession(), PROJECT_KEY).get();
assertThat(db.favorites().hasNoFavorite(createdProject)).isTrue();
}

@Test
public void do_no_add_favorite_when_already_100_favorite_projects_and_no_project_creator_permission_on_permission_template() {
UserDto user = db.users().insertUser();
rangeClosed(1, 100).forEach(i -> db.favorites().add(db.components().insertPrivateProject().getMainBranchComponent(), user.getUuid(), user.getLogin()));
rangeClosed(1, 100).forEach(i -> db.favorites().add(db.components().insertPrivateProject().getProjectDto(), user.getUuid(), user.getLogin()));
userSession
.logIn(user)
.addPermission(GlobalPermission.SCAN)
@@ -219,9 +217,9 @@ public class ReportSubmitterIT {
when(permissionTemplateService.wouldUserHaveScanPermissionWithDefaultTemplate(any(DbSession.class), any(), eq(PROJECT_KEY))).thenReturn(true);
when(permissionTemplateService.hasDefaultTemplateWithPermissionOnProjectCreator(any(DbSession.class), any(ComponentDto.class))).thenReturn(true);

underTest.submit(PROJECT_KEY, PROJECT_NAME, emptyMap(), IOUtils.toInputStream("{binary}"));
underTest.submit(PROJECT_KEY, PROJECT_NAME, emptyMap(), IOUtils.toInputStream("{binary}", UTF_8));

ComponentDto createdProject = db.getDbClient().componentDao().selectByKey(db.getSession(), PROJECT_KEY).get();
ProjectDto createdProject = db.getDbClient().projectDao().selectProjectByKey(db.getSession(), PROJECT_KEY).get();
assertThat(db.favorites().hasNoFavorite(createdProject)).isTrue();
}

@@ -234,7 +232,7 @@ public class ReportSubmitterIT {
when(permissionTemplateService.wouldUserHaveScanPermissionWithDefaultTemplate(any(DbSession.class), any(), eq(PROJECT_KEY)))
.thenReturn(true);

underTest.submit(PROJECT_KEY, PROJECT_NAME, emptyMap(), IOUtils.toInputStream("{binary}"));
underTest.submit(PROJECT_KEY, PROJECT_NAME, emptyMap(), IOUtils.toInputStream("{binary}", UTF_8));

verify(queue).submit(any(CeTaskSubmit.class));
}
@@ -245,7 +243,7 @@ public class ReportSubmitterIT {
userSession.addPermission(SCAN);
mockSuccessfulPrepareSubmitCall();

underTest.submit(project.getKey(), project.name(), emptyMap(), IOUtils.toInputStream("{binary}"));
underTest.submit(project.getKey(), project.name(), emptyMap(), IOUtils.toInputStream("{binary}", UTF_8));

verify(queue).submit(any(CeTaskSubmit.class));
}
@@ -256,7 +254,7 @@ public class ReportSubmitterIT {
userSession.addProjectPermission(SCAN.getKey(), project);
mockSuccessfulPrepareSubmitCall();

underTest.submit(project.getKey(), project.name(), emptyMap(), IOUtils.toInputStream("{binary}"));
underTest.submit(project.getKey(), project.name(), emptyMap(), IOUtils.toInputStream("{binary}", UTF_8));

verify(queue).submit(any(CeTaskSubmit.class));
}

+ 9
- 8
server/sonar-webserver-webapi/src/it/java/org/sonar/server/component/ComponentUpdaterIT.java View File

@@ -33,6 +33,7 @@ import org.sonar.db.DbTester;
import org.sonar.db.component.BranchDto;
import org.sonar.db.component.BranchType;
import org.sonar.db.component.ComponentDto;
import org.sonar.db.project.ProjectDto;
import org.sonar.db.user.UserDto;
import org.sonar.server.es.ProjectIndexer;
import org.sonar.server.es.TestProjectIndexers;
@@ -234,7 +235,7 @@ public class ComponentUpdaterIT {
when(permissionTemplateService.hasDefaultTemplateWithPermissionOnProjectCreator(any(DbSession.class), any(ComponentDto.class)))
.thenReturn(true);

ComponentDto dto = underTest.create(db.getSession(), project, userDto.getUuid(), userDto.getLogin()).mainBranchComponent();
ProjectDto dto = underTest.create(db.getSession(), project, userDto.getUuid(), userDto.getLogin()).projectDto();

assertThat(db.favorites().hasFavorite(dto, userDto.getUuid())).isTrue();
}
@@ -242,7 +243,7 @@ public class ComponentUpdaterIT {
@Test
public void do_not_add_project_to_user_favorites_if_project_creator_is_defined_in_permission_template_and_already_100_favorites() {
UserDto user = db.users().insertUser();
rangeClosed(1, 100).forEach(i -> db.favorites().add(db.components().insertPrivateProject().getMainBranchComponent(), user.getUuid(), user.getLogin()));
rangeClosed(1, 100).forEach(i -> db.favorites().add(db.components().insertPrivateProject().getProjectDto(), user.getUuid(), user.getLogin()));
NewComponent project = NewComponent.newComponentBuilder()
.setKey(DEFAULT_PROJECT_KEY)
.setName(DEFAULT_PROJECT_NAME)
@@ -250,34 +251,34 @@ public class ComponentUpdaterIT {
when(permissionTemplateService.hasDefaultTemplateWithPermissionOnProjectCreator(eq(db.getSession()), any(ComponentDto.class)))
.thenReturn(true);

ComponentDto dto = underTest.create(db.getSession(),
ProjectDto dto = underTest.create(db.getSession(),
project,
user.getUuid(),
user.getLogin()).mainBranchComponent();
user.getLogin()).projectDto();

assertThat(db.favorites().hasFavorite(dto, user.getUuid())).isFalse();
}

@Test
public void does_not_add_project_to_favorite_when_anonymously_created() {
ComponentDto project = underTest.create(db.getSession(),
ProjectDto project = underTest.create(db.getSession(),
NewComponent.newComponentBuilder()
.setKey(DEFAULT_PROJECT_KEY)
.setName(DEFAULT_PROJECT_NAME)
.build(),
null, null).mainBranchComponent();
null, null).projectDto();

assertThat(db.favorites().hasNoFavorite(project)).isTrue();
}

@Test
public void does_not_add_project_to_favorite_when_project_has_no_permission_on_template() {
ComponentDto project = underTest.create(db.getSession(),
ProjectDto project = underTest.create(db.getSession(),
NewComponent.newComponentBuilder()
.setKey(DEFAULT_PROJECT_KEY)
.setName(DEFAULT_PROJECT_NAME)
.build(),
null, null).mainBranchComponent();
null, null).projectDto();

assertThat(db.favorites().hasNoFavorite(project)).isTrue();
}

+ 2
- 1
server/sonar-webserver-webapi/src/it/java/org/sonar/server/component/ws/AppActionIT.java View File

@@ -26,6 +26,7 @@ import org.sonar.api.server.ws.WebService;
import org.sonar.db.DbTester;
import org.sonar.db.component.ComponentDto;
import org.sonar.db.metric.MetricDto;
import org.sonar.db.project.ProjectDto;
import org.sonar.server.component.TestComponentFinder;
import org.sonar.server.exceptions.ForbiddenException;
import org.sonar.server.exceptions.NotFoundException;
@@ -219,7 +220,7 @@ public class AppActionIT {

@Test
public void component_is_favorite() {
ComponentDto project = db.components().insertPrivateProject().getMainBranchComponent();
ProjectDto project = db.components().insertPrivateProject().getProjectDto();
userSession.logIn("john").addProjectPermission(USER, project);
db.favorites().add(project, userSession.getUuid(), userSession.getLogin());


+ 42
- 43
server/sonar-webserver-webapi/src/it/java/org/sonar/server/component/ws/SuggestionsActionIT.java View File

@@ -34,7 +34,9 @@ import org.sonar.core.util.stream.MoreCollectors;
import org.sonar.db.DbTester;
import org.sonar.db.component.ComponentDto;
import org.sonar.db.component.ComponentTesting;
import org.sonar.db.component.ProjectData;
import org.sonar.db.component.ResourceTypesRule;
import org.sonar.db.project.ProjectDto;
import org.sonar.server.component.index.ComponentIndex;
import org.sonar.server.component.index.ComponentIndexer;
import org.sonar.server.es.EsTester;
@@ -68,8 +70,6 @@ import static org.sonar.api.resources.Qualifiers.UNIT_TEST_FILE;
import static org.sonar.api.resources.Qualifiers.VIEW;
import static org.sonar.api.web.UserRole.USER;
import static org.sonar.db.component.ComponentTesting.newFileDto;
import static org.sonar.db.component.ComponentTesting.newPrivateProjectDto;
import static org.sonar.db.component.ComponentTesting.newPublicProjectDto;
import static org.sonar.server.component.ws.SuggestionsAction.PARAM_MORE;
import static org.sonar.server.component.ws.SuggestionsAction.PARAM_QUERY;
import static org.sonar.server.component.ws.SuggestionsAction.PARAM_RECENTLY_BROWSED;
@@ -132,8 +132,8 @@ public class SuggestionsActionIT {

@Test
public void test_example_json_response() {
ComponentDto project1 = db.components().insertPublicProject(p -> p.setKey("org.sonarsource:sonarqube").setName("SonarSource :: SonarQube")).getMainBranchComponent();
ComponentDto project2 = db.components().insertPublicProject(p -> p.setKey("org.sonarsource:sonarlint").setName("SonarSource :: SonarLint")).getMainBranchComponent();
ProjectDto project1 = db.components().insertPublicProject(p -> p.setKey("org.sonarsource:sonarqube").setName("SonarSource :: SonarQube")).getProjectDto();
ProjectDto project2 = db.components().insertPublicProject(p -> p.setKey("org.sonarsource:sonarlint").setName("SonarSource :: SonarLint")).getProjectDto();
componentIndexer.indexAll();
authorizationIndexerTester.allowOnlyAnyone(project1);
authorizationIndexerTester.allowOnlyAnyone(project2);
@@ -150,7 +150,7 @@ public class SuggestionsActionIT {

@Test
public void suggestions_without_query_should_contain_recently_browsed() {
ComponentDto project = db.components().insertPrivateProject().getMainBranchComponent();
ProjectDto project = db.components().insertPrivateProject().getProjectDto();

componentIndexer.indexAll();
userSessionRule.addProjectPermission(USER, project);
@@ -175,7 +175,7 @@ public class SuggestionsActionIT {

@Test
public void suggestions_without_query_should_contain_recently_browsed_public_project() {
ComponentDto project = db.components().insertPublicProject().getMainBranchComponent();
ProjectDto project = db.components().insertPublicProject().getProjectDto();

componentIndexer.indexAll();

@@ -199,7 +199,7 @@ public class SuggestionsActionIT {

@Test
public void suggestions_without_query_should_not_contain_recently_browsed_without_permission() {
ComponentDto project = db.components().insertPrivateProject().getMainBranchComponent();
ProjectDto project = db.components().insertPrivateProject().getProjectDto();

componentIndexer.indexAll();

@@ -215,7 +215,7 @@ public class SuggestionsActionIT {

@Test
public void suggestions_without_query_should_contain_favorites() {
ComponentDto project = db.components().insertPrivateProject().getMainBranchComponent();
ProjectDto project = db.components().insertPrivateProject().getProjectDto();
doReturn(singletonList(project)).when(favoriteFinder).list();

componentIndexer.indexAll();
@@ -240,7 +240,7 @@ public class SuggestionsActionIT {

@Test
public void suggestions_without_query_should_not_contain_favorites_without_permission() {
ComponentDto project = db.components().insertPrivateProject().getMainBranchComponent();
ProjectDto project = db.components().insertPrivateProject().getProjectDto();
doReturn(singletonList(project)).when(favoriteFinder).list();

componentIndexer.indexAll();
@@ -256,7 +256,7 @@ public class SuggestionsActionIT {

@Test
public void suggestions_without_query_should_contain_recently_browsed_favorites() {
ComponentDto project = db.components().insertPrivateProject().getMainBranchComponent();
ProjectDto project = db.components().insertPrivateProject().getProjectDto();
doReturn(singletonList(project)).when(favoriteFinder).list();

componentIndexer.indexAll();
@@ -282,7 +282,7 @@ public class SuggestionsActionIT {

@Test
public void suggestions_without_query_should_not_contain_matches_that_are_neither_favorites_nor_recently_browsed() {
ComponentDto project = db.components().insertPrivateProject().getMainBranchComponent();
ProjectDto project = db.components().insertPrivateProject().getProjectDto();

componentIndexer.indexAll();
userSessionRule.addProjectPermission(USER, project);
@@ -300,10 +300,10 @@ public class SuggestionsActionIT {

@Test
public void suggestions_without_query_should_order_results() {
ComponentDto project1 = db.components().insertPrivateProject(p -> p.setName("Alpha")).getMainBranchComponent();
ComponentDto project2 = db.components().insertPrivateProject(p -> p.setName("Bravo")).getMainBranchComponent();
ComponentDto project3 = db.components().insertPrivateProject(p -> p.setName("Charlie")).getMainBranchComponent();
ComponentDto project4 = db.components().insertPrivateProject(p -> p.setName("Delta")).getMainBranchComponent();
ProjectDto project1 = db.components().insertPrivateProject(p -> p.setName("Alpha")).getProjectDto();
ProjectDto project2 = db.components().insertPrivateProject(p -> p.setName("Bravo")).getProjectDto();
ProjectDto project3 = db.components().insertPrivateProject(p -> p.setName("Charlie")).getProjectDto();
ProjectDto project4 = db.components().insertPrivateProject(p -> p.setName("Delta")).getProjectDto();
doReturn(asList(project4, project2)).when(favoriteFinder).list();

componentIndexer.indexAll();
@@ -314,7 +314,7 @@ public class SuggestionsActionIT {

SuggestionsWsResponse response = ws.newRequest()
.setMethod("POST")
.setParam(PARAM_RECENTLY_BROWSED, Stream.of(project3, project1).map(ComponentDto::getKey).collect(joining(",")))
.setParam(PARAM_RECENTLY_BROWSED, Stream.of(project3, project1).map(ProjectDto::getKey).collect(joining(",")))
.executeProtobuf(SuggestionsWsResponse.class);

// assert order of keys
@@ -330,13 +330,13 @@ public class SuggestionsActionIT {

@Test
public void suggestions_without_query_should_return_empty_qualifiers() {
ComponentDto project = db.components().insertPrivateProject().getMainBranchComponent();
componentIndexer.indexOnAnalysis(project.branchUuid());
userSessionRule.addProjectPermission(USER, project);
ProjectData project = db.components().insertPrivateProject();
componentIndexer.indexOnAnalysis(project.getMainBranchDto().getUuid());
userSessionRule.addProjectPermission(USER, project.getProjectDto());

SuggestionsWsResponse response = ws.newRequest()
.setMethod("POST")
.setParam(PARAM_RECENTLY_BROWSED, project.getKey())
.setParam(PARAM_RECENTLY_BROWSED, project.getProjectDto().getKey())
.executeProtobuf(SuggestionsWsResponse.class);

assertThat(response.getResultsList())
@@ -348,13 +348,13 @@ public class SuggestionsActionIT {
@Test
public void suggestions_should_filter_allowed_qualifiers() {
resourceTypes.setAllQualifiers(PROJECT, FILE, UNIT_TEST_FILE);
ComponentDto project = db.components().insertPrivateProject().getMainBranchComponent();
componentIndexer.indexOnAnalysis(project.branchUuid());
userSessionRule.addProjectPermission(USER, project);
ProjectData project = db.components().insertPrivateProject();
componentIndexer.indexOnAnalysis(project.getMainBranchDto().getUuid());
userSessionRule.addProjectPermission(USER, project.getProjectDto());

SuggestionsWsResponse response = ws.newRequest()
.setMethod("POST")
.setParam(PARAM_RECENTLY_BROWSED, project.getKey())
.setParam(PARAM_RECENTLY_BROWSED, project.getProjectDto().getKey())
.executeProtobuf(SuggestionsWsResponse.class);

assertThat(response.getResultsList())
@@ -364,7 +364,7 @@ public class SuggestionsActionIT {

@Test
public void exact_match_in_one_qualifier() {
ComponentDto project = db.components().insertPrivateProject().getMainBranchComponent();
ProjectDto project = db.components().insertPrivateProject().getProjectDto();

componentIndexer.indexAll();
authorizationIndexerTester.allowOnlyAnyone(project);
@@ -389,12 +389,12 @@ public class SuggestionsActionIT {

@Test
public void should_not_return_suggestion_on_non_existing_project() {
ComponentDto project = db.components().insertPrivateProject().getMainBranchComponent();
ProjectDto project = db.components().insertPrivateProject().getProjectDto();

componentIndexer.indexAll();
authorizationIndexerTester.allowOnlyAnyone(project);

db.getDbClient().purgeDao().deleteProject(db.getSession(), project.uuid(), PROJECT, project.name(), project.getKey());
db.getDbClient().purgeDao().deleteProject(db.getSession(), project.getUuid(), PROJECT, project.getName(), project.getKey());
db.commit();

SuggestionsWsResponse response = ws.newRequest()
@@ -410,7 +410,7 @@ public class SuggestionsActionIT {

@Test
public void must_not_search_if_no_valid_tokens_are_provided() {
ComponentDto project = db.components().insertPrivateProject(p -> p.setName("SonarQube")).getMainBranchComponent();
ProjectDto project = db.components().insertPrivateProject(p -> p.setName("SonarQube")).getProjectDto();

componentIndexer.indexAll();
authorizationIndexerTester.allowOnlyAnyone(project);
@@ -436,7 +436,7 @@ public class SuggestionsActionIT {

@Test
public void should_warn_about_short_inputs_but_return_results_based_on_other_terms() {
ComponentDto project = db.components().insertPrivateProject(p -> p.setName("SonarQube")).getMainBranchComponent();
ProjectDto project = db.components().insertPrivateProject(p -> p.setName("SonarQube")).getProjectDto();

componentIndexer.indexAll();
authorizationIndexerTester.allowOnlyAnyone(project);
@@ -455,9 +455,9 @@ public class SuggestionsActionIT {

@Test
public void should_contain_component_names() {
ComponentDto project1 = db.components().insertPrivateProject(p -> p.setName("Project1")).getMainBranchComponent();
componentIndexer.indexOnAnalysis(project1.branchUuid());
authorizationIndexerTester.allowOnlyAnyone(project1);
ProjectData project1 = db.components().insertPrivateProject(p -> p.setName("Project1"));
componentIndexer.indexOnAnalysis(project1.getMainBranchDto().getUuid());
authorizationIndexerTester.allowOnlyAnyone(project1.getProjectDto());

SuggestionsWsResponse response = ws.newRequest()
.setMethod("POST")
@@ -467,7 +467,7 @@ public class SuggestionsActionIT {
assertThat(response.getResultsList())
.flatExtracting(Category::getItemsList)
.extracting(Suggestion::getKey, Suggestion::getName)
.containsExactlyInAnyOrder(tuple(project1.getKey(), project1.name()));
.containsExactlyInAnyOrder(tuple(project1.getProjectDto().getKey(), project1.getProjectDto().getName()));
}

@Test
@@ -492,13 +492,13 @@ public class SuggestionsActionIT {

@Test
public void should_mark_favorite_items() {
ComponentDto favouriteProject = db.components().insertPrivateProject(p -> p.setName("Project1")).getMainBranchComponent();
ComponentDto nonFavouriteProject = db.components().insertPublicProject(p -> p.setName("Project2")).getMainBranchComponent();
ProjectData favouriteProject = db.components().insertPrivateProject(p -> p.setName("Project1"));
ProjectData nonFavouriteProject = db.components().insertPublicProject(p -> p.setName("Project2"));

doReturn(singletonList(favouriteProject)).when(favoriteFinder).list();
componentIndexer.indexOnAnalysis(favouriteProject.branchUuid());
componentIndexer.indexOnAnalysis(nonFavouriteProject.branchUuid());
authorizationIndexerTester.allowOnlyAnyone(favouriteProject, nonFavouriteProject);
doReturn(singletonList(favouriteProject.getProjectDto())).when(favoriteFinder).list();
componentIndexer.indexOnAnalysis(favouriteProject.getMainBranchDto().getUuid());
componentIndexer.indexOnAnalysis(nonFavouriteProject.getMainBranchDto().getUuid());
authorizationIndexerTester.allowOnlyAnyone(favouriteProject.getProjectDto(), nonFavouriteProject.getProjectDto());

SuggestionsWsResponse response = ws.newRequest()
.setMethod("POST")
@@ -508,7 +508,7 @@ public class SuggestionsActionIT {
assertThat(response.getResultsList())
.flatExtracting(Category::getItemsList)
.extracting(Suggestion::getKey, Suggestion::getIsFavorite)
.containsExactly(tuple(favouriteProject.getKey(), true), tuple(nonFavouriteProject.getKey(), false));
.containsExactly(tuple(favouriteProject.getProjectDto().getKey(), true), tuple(nonFavouriteProject.getProjectDto().getKey(), false));
}

@Test
@@ -553,7 +553,6 @@ public class SuggestionsActionIT {
.containsExactlyInAnyOrder(
tuple(SuggestionCategory.APP.getName(), false),
tuple(SuggestionCategory.VIEW.getName(), false),
tuple(SuggestionCategory.SUBVIEW.getName(), false),
tuple(SuggestionCategory.PROJECT.getName(), false));
}

@@ -677,8 +676,8 @@ public class SuggestionsActionIT {
boolean useQuery) {
String namePrefix = "MyProject";

List<ComponentDto> projects = range(0, numberOfProjects)
.mapToObj(i -> db.components().insertPublicProject(p -> p.setName(namePrefix + i)).getMainBranchComponent())
List<ProjectDto> projects = range(0, numberOfProjects)
.mapToObj(i -> db.components().insertPublicProject(p -> p.setName(namePrefix + i)).getProjectDto())
.collect(Collectors.toList());

componentIndexer.indexAll();

+ 6
- 29
server/sonar-webserver-webapi/src/it/java/org/sonar/server/favorite/ws/AddActionIT.java View File

@@ -29,6 +29,7 @@ import org.sonar.db.DbClient;
import org.sonar.db.DbSession;
import org.sonar.db.DbTester;
import org.sonar.db.component.ComponentDto;
import org.sonar.db.project.ProjectDto;
import org.sonar.db.property.PropertyDto;
import org.sonar.db.property.PropertyQuery;
import org.sonar.db.user.UserDto;
@@ -67,7 +68,7 @@ public class AddActionIT {

@Test
public void add_a_project() {
ComponentDto project = db.components().insertPrivateProject().getMainBranchComponent();
ProjectDto project = db.components().insertPrivateProject().getProjectDto();
UserDto user = db.users().insertUser();
userSession.logIn(user).addProjectPermission(USER, project);

@@ -82,7 +83,7 @@ public class AddActionIT {
PropertyDto favorite = favorites.get(0);
assertThat(favorite)
.extracting(PropertyDto::getComponentUuid, PropertyDto::getUserUuid, PropertyDto::getKey)
.containsOnly(project.uuid(), user.getUuid(), "favourite");
.containsOnly(project.getUuid(), user.getUuid(), "favourite");
}

@Test
@@ -104,24 +105,12 @@ public class AddActionIT {

@Test
public void fail_when_user_is_not_authenticated() {
ComponentDto project = db.components().insertPrivateProject().getMainBranchComponent();
ProjectDto project = db.components().insertPrivateProject().getProjectDto();

assertThatThrownBy(() -> call(project.getKey()))
.isInstanceOf(UnauthorizedException.class);
}

@Test
public void fail_on_directory() {
ComponentDto project = db.components().insertPrivateProject().getMainBranchComponent();
ComponentDto directory = db.components().insertComponent(newDirectory(project, "dir"));
UserDto user = db.users().insertUser();
userSession.logIn(user).addProjectPermission(USER, project);

assertThatThrownBy(() -> call(directory.getKey()))
.isInstanceOf(IllegalArgumentException.class)
.hasMessage("Only components with qualifiers TRK, VW, SVW, APP are supported");
}

@Test
public void fail_on_file() {
ComponentDto project = db.components().insertPrivateProject().getMainBranchComponent();
@@ -130,20 +119,8 @@ public class AddActionIT {
userSession.logIn(user).addProjectPermission(USER, project);

assertThatThrownBy(() -> call(file.getKey()))
.isInstanceOf(IllegalArgumentException.class)
.hasMessage("Only components with qualifiers TRK, VW, SVW, APP are supported");
}

@Test
public void fail_on_unit_test_file() {
ComponentDto project = db.components().insertPrivateProject().getMainBranchComponent();
ComponentDto unitTestFile = db.components().insertComponent(newFileDto(project).setQualifier(UNIT_TEST_FILE));
UserDto user = db.users().insertUser();
userSession.logIn(user).addProjectPermission(USER, project);

assertThatThrownBy(() -> call(unitTestFile.getKey()))
.isInstanceOf(IllegalArgumentException.class)
.hasMessage("Only components with qualifiers TRK, VW, SVW, APP are supported");
.isInstanceOf(NotFoundException.class)
.hasMessage("Entity with key '" + file.getKey() + "' not found");
}

@Test

+ 7
- 13
server/sonar-webserver-webapi/src/it/java/org/sonar/server/favorite/ws/RemoveActionIT.java View File

@@ -26,7 +26,7 @@ import org.junit.Test;
import org.sonar.api.server.ws.WebService;
import org.sonar.db.DbClient;
import org.sonar.db.DbTester;
import org.sonar.db.component.ComponentDto;
import org.sonar.db.project.ProjectDto;
import org.sonar.db.user.UserDto;
import org.sonar.server.component.TestComponentFinder;
import org.sonar.server.exceptions.NotFoundException;
@@ -37,13 +37,10 @@ import org.sonar.server.ws.TestRequest;
import org.sonar.server.ws.TestResponse;
import org.sonar.server.ws.WsActionTester;

import static java.lang.String.format;
import static java.net.HttpURLConnection.HTTP_NO_CONTENT;
import static java.util.Optional.ofNullable;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatThrownBy;
import static org.sonar.db.component.ComponentTesting.newFileDto;
import static org.sonar.db.component.ComponentTesting.newPrivateProjectDto;
import static org.sonar.server.favorite.ws.FavoritesWsParameters.PARAM_COMPONENT;

public class RemoveActionIT {
@@ -67,25 +64,22 @@ public class RemoveActionIT {

@Test
public void remove_a_favorite_project() {
ComponentDto project = insertProjectAndPermissions();
ComponentDto file = db.components().insertComponent(newFileDto(project));
ProjectDto project = insertProjectAndPermissions();
db.favorites().add(project, user.getUuid(), user.getLogin());
db.favorites().add(file, user.getUuid(), user.getLogin());

TestResponse result = call(PROJECT_KEY);

assertThat(result.getStatus()).isEqualTo(HTTP_NO_CONTENT);
assertThat(db.favorites().hasFavorite(project, user.getUuid())).isFalse();
assertThat(db.favorites().hasFavorite(file, user.getUuid())).isTrue();
}

@Test
public void fail_if_not_already_a_favorite() {
ComponentDto componentDto = insertProjectAndPermissions();
ProjectDto componentDto = insertProjectAndPermissions();

assertThatThrownBy(() -> call(PROJECT_KEY))
.isInstanceOf(IllegalArgumentException.class)
.hasMessage("Component '" + PROJECT_KEY + "' (uuid: "+componentDto.uuid()+") is not a favorite");
.hasMessage("Component '" + PROJECT_KEY + "' (uuid: " + componentDto.getUuid() + ") is not a favorite");
}

@Test
@@ -115,11 +109,11 @@ public class RemoveActionIT {
assertThat(param.isRequired()).isTrue();
}

private ComponentDto insertProject() {
return db.components().insertPrivateProject(PROJECT_UUID, c -> c.setKey(PROJECT_KEY)).getMainBranchComponent();
private ProjectDto insertProject() {
return db.components().insertPrivateProject(PROJECT_UUID, c -> c.setKey(PROJECT_KEY)).getProjectDto();
}

private ComponentDto insertProjectAndPermissions() {
private ProjectDto insertProjectAndPermissions() {
userSession.logIn(user);

return insertProject();

+ 17
- 22
server/sonar-webserver-webapi/src/it/java/org/sonar/server/favorite/ws/SearchActionIT.java View File

@@ -31,6 +31,7 @@ import org.sonar.db.DbClient;
import org.sonar.db.DbTester;
import org.sonar.db.component.ComponentDto;
import org.sonar.db.component.ComponentTesting;
import org.sonar.db.project.ProjectDto;
import org.sonar.db.user.UserDto;
import org.sonar.server.exceptions.UnauthorizedException;
import org.sonar.server.favorite.FavoriteFinder;
@@ -76,21 +77,18 @@ public class SearchActionIT {

@Test
public void return_favorites() {
ComponentDto project = newPrivateProjectDto("P1").setKey("K1").setName("N1");
addComponent(project);
addComponent(newFileDto(project).setKey("K11").setName("N11"));
addComponent(newPrivateProjectDto("P2").setKey("K2").setName("N2"));
addPermissionAndFavorite(db.components().insertPrivateProject("P1", c -> c.setKey("K1").setName("N1")).getProjectDto());
addPermissionAndFavorite(db.components().insertPrivateProject("P2", c -> c.setKey("K2").setName("N2")).getProjectDto());

SearchResponse result = call();

assertThat(result.getPaging())
.extracting(Paging::getPageIndex, Paging::getPageSize, Paging::getTotal)
.containsExactly(1, 100, 3);
.containsExactly(1, 100, 2);
assertThat(result.getFavoritesList())
.extracting(Favorite::getKey, Favorite::getName, Favorite::getQualifier)
.containsOnly(
tuple("K1", "N1", PROJECT),
tuple("K11", "N11", FILE),
tuple("K2", "N2", PROJECT));
}

@@ -104,8 +102,8 @@ public class SearchActionIT {

@Test
public void filter_authorized_components() {
addComponent(ComponentTesting.newPrivateProjectDto().setKey("K1"));
ComponentDto unauthorizedProject = db.components().insertComponent(ComponentTesting.newPrivateProjectDto());
addPermissionAndFavorite(db.components().insertPrivateProject(c -> c.setKey("K1")).getProjectDto());
ProjectDto unauthorizedProject = db.components().insertPrivateProject().getProjectDto();
db.favorites().add(unauthorizedProject, userUuid, userLogin);

SearchResponse result = call();
@@ -117,8 +115,8 @@ public class SearchActionIT {
@Test
public void paginate_results() {
IntStream.rangeClosed(1, 9)
.forEach(i -> addComponent(ComponentTesting.newPrivateProjectDto().setKey("K" + i).setName("N" + i)));
ComponentDto unauthorizedProject = db.components().insertComponent(ComponentTesting.newPrivateProjectDto());
.forEach(i -> addPermissionAndFavorite(db.components().insertPrivateProject(c -> c.setKey("K" + i).setName("N" + i)).getProjectDto()));
ProjectDto unauthorizedProject = db.components().insertPrivateProject().getProjectDto();
db.favorites().add(unauthorizedProject, userUuid, userLogin);

SearchResponse result = call(2, 3);
@@ -127,14 +125,12 @@ public class SearchActionIT {
assertThat(result.getFavoritesList())
.extracting(Favorite::getKey)
.containsExactly("K4", "K5", "K6");

}

@Test
public void return_only_users_favorite() {
addComponent(ComponentTesting.newPrivateProjectDto().setKey("K1"));
ComponentDto otherUserFavorite = ComponentTesting.newPrivateProjectDto().setKey("K42");
db.components().insertComponent(otherUserFavorite);
addPermissionAndFavorite(db.components().insertPrivateProject(c -> c.setKey("K1")).getProjectDto());
ProjectDto otherUserFavorite = db.components().insertPrivateProject(c -> c.setKey("K42")).getProjectDto();
db.favorites().add(otherUserFavorite, "42", userLogin);
db.commit();

@@ -145,9 +141,9 @@ public class SearchActionIT {

@Test
public void favorites_ordered_by_name() {
addComponent(ComponentTesting.newPrivateProjectDto().setName("N2"));
addComponent(ComponentTesting.newPrivateProjectDto().setName("N3"));
addComponent(ComponentTesting.newPrivateProjectDto().setName("N1"));
addPermissionAndFavorite(db.components().insertPrivateProject(c -> c.setName("N2")).getProjectDto());
addPermissionAndFavorite(db.components().insertPrivateProject(c -> c.setName("N3")).getProjectDto());
addPermissionAndFavorite(db.components().insertPrivateProject(c -> c.setName("N1")).getProjectDto());

SearchResponse result = call();

@@ -157,9 +153,9 @@ public class SearchActionIT {

@Test
public void json_example() {
addComponent(ComponentTesting.newPrivateProjectDto().setKey("K1").setName("Samba"));
addComponent(ComponentTesting.newPrivateProjectDto().setKey("K2").setName("Apache HBase"));
addComponent(ComponentTesting.newPrivateProjectDto().setKey("K3").setName("JDK9"));
addPermissionAndFavorite(db.components().insertPrivateProject(c -> c.setKey("K1").setName("Samba")).getProjectDto());
addPermissionAndFavorite(db.components().insertPrivateProject(c -> c.setKey("K2").setName("Apache HBase")).getProjectDto());
addPermissionAndFavorite(db.components().insertPrivateProject(c -> c.setKey("K3").setName("JDK9")).getProjectDto());

String result = ws.newRequest().execute().getInput();

@@ -183,8 +179,7 @@ public class SearchActionIT {
.isInstanceOf(UnauthorizedException.class);
}

private void addComponent(ComponentDto component) {
db.components().insertComponent(component);
private void addPermissionAndFavorite(ProjectDto component) {
db.favorites().add(component, userUuid, userLogin);
db.commit();
userSession.addProjectPermission(UserRole.USER, component);

+ 4
- 3
server/sonar-webserver-webapi/src/it/java/org/sonar/server/project/ws/CreateActionIT.java View File

@@ -31,6 +31,7 @@ import org.sonar.db.DbSession;
import org.sonar.db.DbTester;
import org.sonar.db.component.BranchDto;
import org.sonar.db.component.ComponentDto;
import org.sonar.db.project.ProjectDto;
import org.sonar.db.user.UserDto;
import org.sonar.server.component.ComponentUpdater;
import org.sonar.server.es.TestProjectIndexers;
@@ -199,7 +200,7 @@ public class CreateActionIT {
.setParam("name", DEFAULT_PROJECT_NAME)
.executeProtobuf(CreateWsResponse.class);

ComponentDto project = db.getDbClient().componentDao().selectByKey(db.getSession(), DEFAULT_PROJECT_KEY).get();
ProjectDto project = db.getDbClient().projectDao().selectProjectByKey(db.getSession(), DEFAULT_PROJECT_KEY).get();
assertThat(db.favorites().hasFavorite(project, user.getUuid())).isTrue();
}

@@ -207,7 +208,7 @@ public class CreateActionIT {
public void do_not_add_project_to_user_favorites_if_project_creator_is_defined_in_permission_template_and_already_100_favorites() {
UserDto user = db.users().insertUser();
when(permissionTemplateService.hasDefaultTemplateWithPermissionOnProjectCreator(any(DbSession.class), any(ComponentDto.class))).thenReturn(true);
rangeClosed(1, 100).forEach(i -> db.favorites().add(db.components().insertPrivateProject().getMainBranchComponent(), user.getUuid(), user.getLogin()));
rangeClosed(1, 100).forEach(i -> db.favorites().add(db.components().insertPrivateProject().getProjectDto(), user.getUuid(), user.getLogin()));
userSession.logIn(user).addPermission(PROVISION_PROJECTS);

ws.newRequest()
@@ -215,7 +216,7 @@ public class CreateActionIT {
.setParam("name", DEFAULT_PROJECT_NAME)
.executeProtobuf(CreateWsResponse.class);

ComponentDto project = db.getDbClient().componentDao().selectByKey(db.getSession(), DEFAULT_PROJECT_KEY).get();
ProjectDto project = db.getDbClient().projectDao().selectProjectByKey(db.getSession(), DEFAULT_PROJECT_KEY).get();
assertThat(db.favorites().hasNoFavorite(project)).isTrue();
}


+ 40
- 120
server/sonar-webserver-webapi/src/it/java/org/sonar/server/setting/ws/ResetActionIT.java View File

@@ -21,6 +21,7 @@ package org.sonar.server.setting.ws;

import java.util.Random;
import javax.annotation.Nullable;
import javax.sound.sampled.Port;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
@@ -34,6 +35,9 @@ import org.sonar.db.DbSession;
import org.sonar.db.DbTester;
import org.sonar.db.component.ComponentDto;
import org.sonar.db.component.ComponentTesting;
import org.sonar.db.entity.EntityDto;
import org.sonar.db.portfolio.PortfolioDto;
import org.sonar.db.project.ProjectDto;
import org.sonar.db.property.PropertyDbTester;
import org.sonar.db.property.PropertyQuery;
import org.sonar.db.user.UserDto;
@@ -79,13 +83,13 @@ public class ResetActionIT {
private final PropertyDefinitions definitions = new PropertyDefinitions(System2.INSTANCE);
private final SettingsUpdater settingsUpdater = new SettingsUpdater(dbClient, definitions);
private final SettingValidations settingValidations = new SettingValidations(definitions, dbClient, i18n);
private ComponentDto project;
private ProjectDto project;
private final ResetAction underTest = new ResetAction(dbClient, componentFinder, settingsUpdater, userSession, definitions, settingValidations);
private final WsActionTester ws = new WsActionTester(underTest);

@Before
public void setUp() {
project = db.components().insertPrivateProject().getMainBranchComponent();
project = db.components().insertPrivateProject().getProjectDto();
}

@Test
@@ -141,7 +145,7 @@ public class ResetActionIT {
logInAsSystemAdministrator();
propertyDb.insertProperties(null, null, null, null,
newGlobalPropertyDto().setKey("foo").setValue("one"));
propertyDb.insertProperties(null, project.getKey(), project.name(), project.qualifier(),
propertyDb.insertProperties(null, project.getKey(), project.getName(), project.getQualifier(),
newComponentPropertyDto(project).setKey("foo").setValue("value"));

executeRequestOnGlobalSetting("foo");
@@ -154,7 +158,7 @@ public class ResetActionIT {
public void ignore_global_setting_when_removing_project_setting() {
logInAsProjectAdmin();
propertyDb.insertProperties(null, null, null, null, newGlobalPropertyDto().setKey("foo").setValue("one"));
propertyDb.insertProperties(null, project.getKey(), project.name(), project.qualifier(),
propertyDb.insertProperties(null, project.getKey(), project.getName(), project.getQualifier(),
newComponentPropertyDto(project).setKey("foo").setValue("value"));

executeRequestOnProjectSetting("foo");
@@ -200,26 +204,6 @@ public class ResetActionIT {
assertGlobalPropertyDoesNotExist("foo");
}

@Test
public void remove_setting_on_branch() {
ComponentDto project = db.components().insertPublicProject().getMainBranchComponent();
String branchName = randomAlphanumeric(248);
ComponentDto branch = db.components().insertProjectBranch(project, b -> b.setKey(branchName));
definitions.addComponent(PropertyDefinition.builder("foo").onQualifiers(PROJECT).build());
propertyDb.insertProperties(null, branch.name(), null, null, newComponentPropertyDto(branch).setKey("foo").setValue("value"));
userSession.logIn().addProjectPermission(ADMIN, project);
userSession.addProjectBranchMapping(project.uuid(), branch);

ws.newRequest()
.setMediaType(MediaTypes.PROTOBUF)
.setParam("keys", "foo")
.setParam("component", branch.getKey())
.setParam("branch", branchName)
.execute();

assertProjectPropertyDoesNotExist(branch, "foo");
}

@Test
public void empty_204_response() {
logInAsSystemAdministrator();
@@ -256,9 +240,7 @@ public class ResetActionIT {
userSession.logIn().addProjectPermission(USER, project);
definitions.addComponent(PropertyDefinition.builder("foo").build());

assertThatThrownBy(() -> {
executeRequestOnComponentSetting("foo", project);
})
assertThatThrownBy(() -> executeRequestOnComponentSetting("foo", project))
.isInstanceOf(ForbiddenException.class)
.hasMessage("Insufficient privileges");
}
@@ -268,9 +250,7 @@ public class ResetActionIT {
logInAsSystemAdministrator();
definitions.addComponent(PropertyDefinition.builder("foo").build());

assertThatThrownBy(() -> {
executeRequestOnComponentSetting("foo", project);
})
assertThatThrownBy(() -> executeRequestOnComponentSetting("foo", project))
.isInstanceOf(ForbiddenException.class)
.hasMessage("Insufficient privileges");
}
@@ -282,9 +262,7 @@ public class ResetActionIT {
.onlyOnQualifiers(VIEW)
.build());

assertThatThrownBy(() -> {
executeRequestOnGlobalSetting("foo");
})
assertThatThrownBy(() -> executeRequestOnGlobalSetting("foo"))
.isInstanceOf(BadRequestException.class)
.hasMessage("Setting 'foo' cannot be global");
}
@@ -297,9 +275,7 @@ public class ResetActionIT {
.build());
i18n.put("qualifier." + PROJECT, "project");

assertThatThrownBy(() -> {
executeRequestOnComponentSetting("foo", project);
})
assertThatThrownBy(() -> executeRequestOnComponentSetting("foo", project))
.isInstanceOf(BadRequestException.class)
.hasMessage("Setting 'foo' cannot be set on a project");
}
@@ -311,115 +287,55 @@ public class ResetActionIT {
definitions.addComponent(PropertyDefinition.builder("foo").build());
i18n.put("qualifier." + PROJECT, "project");

assertThatThrownBy(() -> {
executeRequestOnComponentSetting("foo", project);
})
assertThatThrownBy(() -> executeRequestOnComponentSetting("foo", project))
.isInstanceOf(BadRequestException.class)
.hasMessage("Setting 'foo' cannot be set on a project");
}

@Test
public void succeed_for_property_without_definition_when_set_on_project_component() {
ComponentDto project = randomPublicOrPrivateProject();
succeedForPropertyWithoutDefinitionAndValidComponent(project, project);
}

@Test
public void fail_for_property_without_definition_when_set_on_directory_component() {
ComponentDto project = randomPublicOrPrivateProject();
ComponentDto directory = db.components().insertComponent(ComponentTesting.newDirectory(project, "A/B"));
failForPropertyWithoutDefinitionOnUnsupportedComponent(project, directory);
}

@Test
public void fail_for_property_without_definition_when_set_on_file_component() {
ComponentDto project = randomPublicOrPrivateProject();
ComponentDto file = db.components().insertComponent(ComponentTesting.newFileDto(project));
failForPropertyWithoutDefinitionOnUnsupportedComponent(project, file);
ProjectDto project = randomPublicOrPrivateProject();
succeedForPropertyWithoutDefinitionAndValidComponent(project);
}

@Test
public void succeed_for_property_without_definition_when_set_on_view_component() {
ComponentDto view = db.components().insertPublicPortfolio();
succeedForPropertyWithoutDefinitionAndValidComponent(view, view);
}

@Test
public void succeed_for_property_without_definition_when_set_on_subview_component() {
ComponentDto view = db.components().insertPublicPortfolio();
ComponentDto subview = db.components().insertComponent(ComponentTesting.newSubPortfolio(view));
succeedForPropertyWithoutDefinitionAndValidComponent(view, subview);
}

@Test
public void fail_for_property_without_definition_when_set_on_projectCopy_component() {
ComponentDto view = db.components().insertPublicPortfolio();
ComponentDto projectCopy = db.components().insertComponent(ComponentTesting.newProjectCopy("a", db.components().insertPrivateProject().getMainBranchComponent(), view));

failForPropertyWithoutDefinitionOnUnsupportedComponent(view, projectCopy);
PortfolioDto view = db.components().insertPublicPortfolioDto();
succeedForPropertyWithoutDefinitionAndValidComponent(view);
}

@Test
public void fail_when_component_not_found() {
assertThatThrownBy(() -> {
ws.newRequest()
assertThatThrownBy(() -> ws.newRequest()
.setParam("keys", "foo")
.setParam("component", "unknown")
.execute();
})
.execute())
.isInstanceOf(NotFoundException.class)
.hasMessage("Component key 'unknown' not found");
}

@Test
public void fail_when_branch_not_found() {
ComponentDto project = db.components().insertPublicProject().getMainBranchComponent();
logInAsProjectAdmin(project);
ComponentDto branch = db.components().insertProjectBranch(project);
String settingKey = "not_allowed_on_branch";

assertThatThrownBy(() -> {
ws.newRequest()
.setParam("keys", settingKey)
.setParam("component", branch.getKey())
.setParam("branch", "unknown")
.execute();
})
.isInstanceOf(NotFoundException.class)
.hasMessage(format("Component '%s' on branch 'unknown' not found", branch.getKey()));
}

@Test
public void fail_when_setting_key_is_defined_in_sonar_properties() {
ComponentDto project = db.components().insertPrivateProject().getMainBranchComponent();
ProjectDto project = db.components().insertPrivateProject().getProjectDto();
logInAsProjectAdmin(project);
String settingKey = ProcessProperties.Property.JDBC_URL.getKey();

assertThatThrownBy(() -> {
ws.newRequest()
assertThatThrownBy(() -> ws.newRequest()
.setParam("keys", settingKey)
.setParam("component", project.getKey())
.execute();
})
.execute())
.isInstanceOf(IllegalArgumentException.class)
.hasMessage(format("Setting '%s' can only be used in sonar.properties", settingKey));
}

private void succeedForPropertyWithoutDefinitionAndValidComponent(ComponentDto root, ComponentDto module) {
logInAsProjectAdmin(root);

executeRequestOnComponentSetting("foo", module);
private void succeedForPropertyWithoutDefinitionAndValidComponent(ProjectDto project) {
logInAsProjectAdmin(project);
executeRequestOnComponentSetting("foo", project);
}

private void failForPropertyWithoutDefinitionOnUnsupportedComponent(ComponentDto root, ComponentDto component) {
i18n.put("qualifier." + component.qualifier(), "QualifierLabel");
logInAsProjectAdmin(root);

assertThatThrownBy(() -> {
executeRequestOnComponentSetting("foo", component);
})
.isInstanceOf(BadRequestException.class)
.hasMessage("Setting 'foo' cannot be set on a QualifierLabel");
private void succeedForPropertyWithoutDefinitionAndValidComponent(PortfolioDto project) {
logInAsProjectAdmin(project);
executeRequestOnComponentSetting("foo", project);
}

private void executeRequestOnGlobalSetting(String key) {
@@ -430,8 +346,8 @@ public class ResetActionIT {
executeRequest(key, project.getKey());
}

private void executeRequestOnComponentSetting(String key, ComponentDto componentDto) {
executeRequest(key, componentDto.getKey());
private void executeRequestOnComponentSetting(String key, EntityDto entity) {
executeRequest(key, entity.getKey());
}

private void executeRequest(String key, @Nullable String componentKey) {
@@ -452,10 +368,14 @@ public class ResetActionIT {
userSession.logIn().addProjectPermission(ADMIN, project);
}

private void logInAsProjectAdmin(ComponentDto root) {
private void logInAsProjectAdmin(ProjectDto root) {
userSession.logIn().addProjectPermission(ADMIN, root);
}

private void logInAsProjectAdmin(PortfolioDto root) {
userSession.logIn().addPortfolioPermission(ADMIN, root);
}

private void assertGlobalPropertyDoesNotExist(String key) {
assertThat(dbClient.propertiesDao().selectGlobalProperty(dbSession, key)).isNull();
}
@@ -464,8 +384,8 @@ public class ResetActionIT {
assertThat(dbClient.propertiesDao().selectGlobalProperty(dbSession, key)).isNotNull();
}

private void assertProjectPropertyDoesNotExist(ComponentDto component, String key) {
assertThat(dbClient.propertiesDao().selectByQuery(PropertyQuery.builder().setComponentUuid(component.uuid()).setKey(key).build(), dbSession)).isEmpty();
private void assertProjectPropertyDoesNotExist(EntityDto entity, String key) {
assertThat(dbClient.propertiesDao().selectByQuery(PropertyQuery.builder().setComponentUuid(entity.getUuid()).setKey(key).build(), dbSession)).isEmpty();
}

private void assertProjectPropertyDoesNotExist(String key) {
@@ -473,7 +393,7 @@ public class ResetActionIT {
}

private void assertProjectPropertyExists(String key) {
assertThat(dbClient.propertiesDao().selectByQuery(PropertyQuery.builder().setComponentUuid(project.uuid()).setKey(key).build(), dbSession)).isNotEmpty();
assertThat(dbClient.propertiesDao().selectByQuery(PropertyQuery.builder().setComponentUuid(project.getUuid()).setKey(key).build(), dbSession)).isNotEmpty();
}

private void assertUserPropertyExists(String key, UserDto user) {
@@ -484,8 +404,8 @@ public class ResetActionIT {
dbSession)).isNotEmpty();
}

private ComponentDto randomPublicOrPrivateProject() {
return new Random().nextBoolean() ? db.components().insertPrivateProject().getMainBranchComponent() : db.components().insertPublicProject().getMainBranchComponent();
private ProjectDto randomPublicOrPrivateProject() {
return new Random().nextBoolean() ? db.components().insertPrivateProject().getProjectDto() : db.components().insertPublicProject().getProjectDto();
}

}

+ 6
- 6
server/sonar-webserver-webapi/src/it/java/org/sonar/server/setting/ws/SetActionIT.java View File

@@ -836,7 +836,7 @@ public class SetActionIT {
@Test
public void succeed_for_property_without_definition_when_set_on_project_component() {
ComponentDto project = randomPublicOrPrivateProject();
succeedForPropertyWithoutDefinitionAndValidComponent(project, project);
succeedForPropertyWithoutDefinitionAndValidComponent(project);
}

@Test
@@ -856,14 +856,14 @@ public class SetActionIT {
@Test
public void succeed_for_property_without_definition_when_set_on_view_component() {
ComponentDto view = db.components().insertPrivatePortfolio();
succeedForPropertyWithoutDefinitionAndValidComponent(view, view);
succeedForPropertyWithoutDefinitionAndValidComponent(view);
}

@Test
public void succeed_for_property_without_definition_when_set_on_subview_component() {
ComponentDto view = db.components().insertPrivatePortfolio();
ComponentDto subview = db.components().insertComponent(ComponentTesting.newSubPortfolio(view));
succeedForPropertyWithoutDefinitionAndValidComponent(view, subview);
failForPropertyWithoutDefinitionOnUnsupportedComponent(view, subview);
}

@Test
@@ -874,12 +874,12 @@ public class SetActionIT {
failForPropertyWithoutDefinitionOnUnsupportedComponent(view, projectCopy);
}

private void succeedForPropertyWithoutDefinitionAndValidComponent(ComponentDto project, ComponentDto module) {
private void succeedForPropertyWithoutDefinitionAndValidComponent(ComponentDto project) {
logInAsProjectAdministrator(project);

callForProjectSettingByKey("my.key", "My Value", module.getKey());
callForProjectSettingByKey("my.key", "My Value", project.getKey());

assertComponentSetting("my.key", "My Value", module.uuid());
assertComponentSetting("my.key", "My Value", project.uuid());
}

private void failForPropertyWithoutDefinitionOnUnsupportedComponent(ComponentDto root, ComponentDto component) {

+ 10
- 9
server/sonar-webserver-webapi/src/it/java/org/sonar/server/setting/ws/SettingsUpdaterIT.java View File

@@ -33,6 +33,7 @@ import org.sonar.db.DbTester;
import org.sonar.db.component.ComponentDbTester;
import org.sonar.db.component.ComponentDto;
import org.sonar.db.component.ComponentTesting;
import org.sonar.db.project.ProjectDto;
import org.sonar.db.property.PropertyDbTester;
import org.sonar.db.property.PropertyQuery;
import org.sonar.db.user.UserDto;
@@ -58,19 +59,19 @@ public class SettingsUpdaterIT {
ComponentDbTester componentDb = new ComponentDbTester(db);

PropertyDefinitions definitions = new PropertyDefinitions(System2.INSTANCE);
ComponentDto project;
ProjectDto project;

SettingsUpdater underTest = new SettingsUpdater(dbClient, definitions);

@Before
public void setUp() {
project = componentDb.insertComponent(ComponentTesting.newPrivateProjectDto());
project = componentDb.insertPrivateProject().getProjectDto();
}

@Test
public void delete_global_settings() {
definitions.addComponent(PropertyDefinition.builder("foo").build());
propertyDb.insertProperties(null, project.getKey(), project.name(), project.qualifier(),
propertyDb.insertProperties(null, project.getKey(), project.getName(), project.getQualifier(),
newComponentPropertyDto(project).setKey("foo").setValue("value"));
propertyDb.insertProperties(null, null, null, null, newGlobalPropertyDto().setKey("foo").setValue("one"));
propertyDb.insertProperties(null, null, null, null, newGlobalPropertyDto().setKey("bar").setValue("two"));
@@ -86,9 +87,9 @@ public class SettingsUpdaterIT {
public void delete_component_settings() {
definitions.addComponent(PropertyDefinition.builder("foo").build());
propertyDb.insertProperties(null, null, null, null, newGlobalPropertyDto().setKey("foo").setValue("value"));
propertyDb.insertProperties(null, project.getKey(), project.name(), project.qualifier(),
propertyDb.insertProperties(null, project.getKey(), project.getName(), project.getQualifier(),
newComponentPropertyDto(project).setKey("foo").setValue("one"));
propertyDb.insertProperties(null, project.getKey(), project.name(), project.qualifier(),
propertyDb.insertProperties(null, project.getKey(), project.getName(), project.getQualifier(),
newComponentPropertyDto(project).setKey("bar").setValue("two"));

underTest.deleteComponentSettings(dbSession, project, "foo", "bar");
@@ -150,7 +151,7 @@ public class SettingsUpdaterIT {
PropertyFieldDefinition.build("key").name("Key").build(),
PropertyFieldDefinition.build("size").name("Size").build()))
.build());
propertyDb.insertProperties(null, project.getKey(), project.name(), project.qualifier(),
propertyDb.insertProperties(null, project.getKey(), project.getName(), project.getQualifier(),
newComponentPropertyDto(project).setKey("foo").setValue("1,2"),
newComponentPropertyDto(project).setKey("foo.1.key").setValue("key1"),
newComponentPropertyDto(project).setKey("foo.1.size").setValue("size1"),
@@ -173,7 +174,7 @@ public class SettingsUpdaterIT {
PropertyFieldDefinition.build("key").name("Key").build(),
PropertyFieldDefinition.build("size").name("Size").build()))
.build());
propertyDb.insertProperties(null, project.getKey(), project.name(), project.qualifier(),
propertyDb.insertProperties(null, project.getKey(), project.getName(), project.getQualifier(),
newComponentPropertyDto(project).setKey("other").setValue("1,2"),
newComponentPropertyDto(project).setKey("other.1.key").setValue("key1"));

@@ -209,11 +210,11 @@ public class SettingsUpdaterIT {
}

private void assertProjectPropertyDoesNotExist(String key) {
assertThat(dbClient.propertiesDao().selectByQuery(PropertyQuery.builder().setComponentUuid(project.uuid()).setKey(key).build(), dbSession)).isEmpty();
assertThat(dbClient.propertiesDao().selectByQuery(PropertyQuery.builder().setComponentUuid(project.getUuid()).setKey(key).build(), dbSession)).isEmpty();
}

private void assertProjectPropertyExists(String key) {
assertThat(dbClient.propertiesDao().selectByQuery(PropertyQuery.builder().setComponentUuid(project.uuid()).setKey(key).build(), dbSession)).isNotEmpty();
assertThat(dbClient.propertiesDao().selectByQuery(PropertyQuery.builder().setComponentUuid(project.getUuid()).setKey(key).build(), dbSession)).isNotEmpty();
}

private void assertUserPropertyExists(String key, UserDto user) {

+ 18
- 114
server/sonar-webserver-webapi/src/it/java/org/sonar/server/setting/ws/ValuesActionIT.java View File

@@ -38,7 +38,10 @@ import org.sonar.api.web.UserRole;
import org.sonar.db.DbClient;
import org.sonar.db.DbTester;
import org.sonar.db.component.ComponentDto;
import org.sonar.db.component.ProjectData;
import org.sonar.db.entity.EntityDto;
import org.sonar.db.permission.GlobalPermission;
import org.sonar.db.project.ProjectDto;
import org.sonar.process.ProcessProperties;
import org.sonar.server.component.TestComponentFinder;
import org.sonar.server.exceptions.ForbiddenException;
@@ -80,11 +83,14 @@ public class ValuesActionIT {
private final PropertyDefinitions definitions = new PropertyDefinitions(System2.INSTANCE);
private final SettingsWsSupport support = new SettingsWsSupport(userSession);
private final WsActionTester wsActionTester = new WsActionTester(new ValuesAction(dbClient, TestComponentFinder.from(db), userSession, definitions, support));
private ComponentDto project;
private ProjectDto project;
private ComponentDto rootComponent;

@Before
public void setUp() {
project = db.components().insertPrivateProject().getMainBranchComponent();
ProjectData projectData = db.components().insertPrivateProject();
project = projectData.getProjectDto();
rootComponent = projectData.getMainBranchComponent();
}

@Test
@@ -240,7 +246,7 @@ public class ValuesActionIT {
PropertyDefinition.builder("property").defaultValue("default").onQualifiers(PROJECT).build());
db.properties().insertProperties(null, null, null,
null, newGlobalPropertyDto().setKey("property").setValue("one"));
db.properties().insertProperties(null, project.getKey(), project.name(), project.qualifier(),
db.properties().insertProperties(null, project.getKey(), project.getName(), project.getQualifier(),
// The property is overriding global value
newComponentPropertyDto(project).setKey("property").setValue("two"));

@@ -259,7 +265,7 @@ public class ValuesActionIT {
PropertyDefinition.builder("project").onQualifiers(PROJECT).build()));
db.properties().insertProperties(null, null, null,
null, newGlobalPropertyDto().setKey("global").setValue("one"));
db.properties().insertProperties(null, project.getKey(), project.name(), project.qualifier(),
db.properties().insertProperties(null, project.getKey(), project.getName(), project.getQualifier(),
newComponentPropertyDto(project).setKey("project").setValue("two"));

ValuesWsResponse result = executeRequestForProjectProperties();
@@ -299,7 +305,7 @@ public class ValuesActionIT {
@Test
public void return_values_of_component_even_if_no_property_definition() {
logInAsProjectUser();
db.properties().insertProperties(null, project.getKey(), project.name(), project.qualifier(),
db.properties().insertProperties(null, project.getKey(), project.getName(), project.getQualifier(),
newComponentPropertyDto(project).setKey("property").setValue("foo"));

ValuesWsResponse response = executeRequestForComponentProperties(project, "property");
@@ -334,31 +340,6 @@ public class ValuesActionIT {
assertThat(result.getSettingsList()).isEmpty();
}

@Test
public void return_inherited_values_on_component() {
logInAsProjectUser();
ComponentDto file = db.components().insertComponent(newFileDto(project));
definitions.addComponents(asList(
PropertyDefinition.builder("defaultProperty").defaultValue("default").onQualifiers(PROJECT).build(),
PropertyDefinition.builder("globalProperty").onQualifiers(PROJECT).build(),
PropertyDefinition.builder("projectProperty").onQualifiers(PROJECT).build(),
PropertyDefinition.builder("componentProperty").onQualifiers(PROJECT).build()));
db.properties().insertProperties(null, null, null, null,
newGlobalPropertyDto().setKey("globalProperty").setValue("global"));
db.properties().insertProperties(null, project.getKey(), project.name(), project.qualifier(),
newComponentPropertyDto(project).setKey("projectProperty").setValue("project"));
db.properties().insertProperties(null, file.getKey(), file.name(), file.qualifier(),
newComponentPropertyDto(file).setKey("componentProperty").setValue("component"));

ValuesWsResponse result = executeRequestForComponentProperties(file, "defaultProperty", "globalProperty", "projectProperty", "componentProperty");

assertThat(result.getSettingsList()).hasSize(4);
assertSetting(result.getSettings(0), "defaultProperty", "default", true);
assertSetting(result.getSettings(1), "globalProperty", "global", true);
assertSetting(result.getSettings(2), "projectProperty", "project", true);
assertSetting(result.getSettings(3), "componentProperty", "component", false);
}

@Test
public void return_inherited_values_on_global_setting() {
logIn();
@@ -375,83 +356,6 @@ public class ValuesActionIT {
assertSetting(result.getSettings(1), "globalProperty", "global", false);
}

@Test
public void return_parent_value() {
logInAsProjectUser();
ComponentDto file = db.components().insertComponent(newFileDto(project));
definitions.addComponents(asList(
PropertyDefinition.builder("foo").defaultValue("default").onQualifiers(PROJECT).build()));
db.properties().insertProperties(null, null, null, null,
newGlobalPropertyDto().setKey("foo").setValue("global"));
db.properties().insertProperties(null, project.getKey(), project.name(), project.qualifier(),
newComponentPropertyDto(project).setKey("foo").setValue("project"));
db.properties().insertProperties(null, file.getKey(), file.name(), file.qualifier(),
newComponentPropertyDto(file).setKey("foo").setValue("file"));

assertParentValue(executeRequestForComponentProperties(file, "foo").getSettings(0), "project");
assertParentValue(executeRequestForComponentProperties(project, "foo").getSettings(0), "global");
assertParentValue(executeRequestForGlobalProperties("foo").getSettings(0), "default");
}

@Test
public void return_parent_field_values() {
logInAsProjectUser();
ComponentDto file = db.components().insertComponent(newFileDto(project));
definitions.addComponent(PropertyDefinition
.builder("foo")
.onQualifiers(PROJECT)
.type(PropertyType.PROPERTY_SET)
.fields(asList(
PropertyFieldDefinition.build("key").name("Key").build(),
PropertyFieldDefinition.build("size").name("Size").build()))
.build());
db.properties().insertPropertySet("foo", null, ImmutableMap.of("key", "keyG1", "size", "sizeG1"));
db.properties().insertPropertySet("foo", project, ImmutableMap.of("key", "keyP1", "size", "sizeP1"));
db.properties().insertPropertySet("foo", file, ImmutableMap.of("key", "keyM1", "size", "sizeM1"));

assertParentFieldValues(executeRequestForComponentProperties(file, "foo").getSettings(0), ImmutableMap.of("key", "keyP1", "size", "sizeP1"));
assertParentFieldValues(executeRequestForComponentProperties(project, "foo").getSettings(0), ImmutableMap.of("key", "keyG1", "size", "sizeG1"));
assertParentFieldValues(executeRequestForGlobalProperties("foo").getSettings(0));
}

@Test
public void return_no_parent_value() {
logInAsProjectUser();
ComponentDto file = db.components().insertComponent(newFileDto(project));
definitions.addComponents(asList(
PropertyDefinition.builder("simple").onQualifiers(PROJECT).build(),
PropertyDefinition.builder("multi").multiValues(true).onQualifiers(PROJECT).build(),
PropertyDefinition.builder("set")
.type(PropertyType.PROPERTY_SET)
.onQualifiers(PROJECT)
.fields(asList(
PropertyFieldDefinition.build("key").name("Key").build(),
PropertyFieldDefinition.build("size").name("Size").build()))
.build()));
db.properties().insertProperties(null, project.getKey(), project.name(), project.qualifier(),
newComponentPropertyDto(project).setKey("simple").setValue("module"),
newComponentPropertyDto(project).setKey("multi").setValue("module1,module2"));
db.properties().insertPropertySet("set", project, ImmutableMap.of("key", "keyM1", "size", "sizeM1"));

assertParentValue(executeRequestForComponentProperties(file, "simple").getSettings(0), null);
assertParentValues(executeRequestForComponentProperties(file, "multi").getSettings(0));
assertParentFieldValues(executeRequestForComponentProperties(file, "set").getSettings(0));
}

@Test
public void return_parent_value_when_no_definition() {
logInAsProjectUser();
ComponentDto file = db.components().insertComponent(newFileDto(project));
db.properties().insertProperties(null, null, null, null,
newGlobalPropertyDto().setKey("foo").setValue("global"));
db.properties().insertProperties(null, project.getKey(), project.name(), project.qualifier(),
newComponentPropertyDto(project).setKey("foo").setValue("project"));

assertParentValue(executeRequestForComponentProperties(file, "foo").getSettings(0), "project");
assertParentValue(executeRequestForComponentProperties(project, "foo").getSettings(0), "global");
assertParentValue(executeRequestForGlobalProperties("foo").getSettings(0), null);
}

@Test
public void return_value_of_deprecated_key() {
logIn();
@@ -527,7 +431,7 @@ public class ValuesActionIT {
PropertyDefinition.builder("secret.secured").onQualifiers(PROJECT).build()));
db.properties().insertProperties(null, null, null, null,
newGlobalPropertyDto().setKey("global.secret.secured").setValue("very secret"));
db.properties().insertProperties(null, project.getKey(), project.name(), project.qualifier(),
db.properties().insertProperties(null, project.getKey(), project.getName(), project.getQualifier(),
newComponentPropertyDto(project).setKey("foo").setValue("one"),
newComponentPropertyDto(project).setKey("secret.secured").setValue("password"));

@@ -542,7 +446,7 @@ public class ValuesActionIT {
userSession
.addProjectPermission(USER, project)
.addProjectPermission(SCAN.getKey(), project);
db.properties().insertProperties(null, project.getKey(), project.name(), project.qualifier(),
db.properties().insertProperties(null, project.getKey(), project.getName(), project.getQualifier(),
newComponentPropertyDto(project).setKey("not-defined.secured").setValue("123"));

ValuesWsResponse result = executeRequestForProjectProperties("not-defined.secured");
@@ -575,7 +479,7 @@ public class ValuesActionIT {
PropertyDefinition.builder("secret.secured").onQualifiers(PROJECT).build()));
db.properties().insertProperties(null, null, null, null,
newGlobalPropertyDto().setKey("global.secret.secured").setValue("very secret"));
db.properties().insertProperties(null, project.getKey(), project.name(), project.qualifier(),
db.properties().insertProperties(null, project.getKey(), project.getName(), project.getQualifier(),
newComponentPropertyDto(project).setKey("foo").setValue("one"),
newComponentPropertyDto(project).setKey("secret.secured").setValue("password"));

@@ -590,7 +494,7 @@ public class ValuesActionIT {
@Test
public void return_secured_settings_even_if_not_defined_when_project_admin() {
logInAsProjectAdmin();
db.properties().insertProperties(null, project.getKey(), project.name(), project.qualifier(),
db.properties().insertProperties(null, project.getKey(), project.getName(), project.getQualifier(),
newComponentPropertyDto(project).setKey("not-defined.secured").setValue("123"));

ValuesWsResponse result = executeRequestForProjectProperties("not-defined.secured");
@@ -676,7 +580,7 @@ public class ValuesActionIT {
definitions.addComponents(asList(
PropertyDefinition.builder("foo").onQualifiers(PROJECT).build(),
PropertyDefinition.builder("secret.secured").onQualifiers(PROJECT).build()));
db.properties().insertProperties(null, project.getKey(), project.name(), project.qualifier(),
db.properties().insertProperties(null, project.getKey(), project.getName(), project.getQualifier(),
newComponentPropertyDto(project).setKey("foo").setValue("one"),
newComponentPropertyDto(project).setKey("secret.secured").setValue("password"));

@@ -847,8 +751,8 @@ public class ValuesActionIT {
assertThat(response.getSetSecuredSettingsList()).contains("my.password.secured");
}

private ValuesWsResponse executeRequestForComponentProperties(ComponentDto componentDto, String... keys) {
return executeRequest(componentDto.getKey(), keys);
private ValuesWsResponse executeRequestForComponentProperties(EntityDto entity, String... keys) {
return executeRequest(entity.getKey(), keys);
}

private ValuesWsResponse executeRequestForProjectProperties(String... keys) {

+ 13
- 1
server/sonar-webserver-webapi/src/main/java/org/sonar/server/component/ComponentUpdater.java View File

@@ -36,6 +36,7 @@ import org.sonar.db.DbSession;
import org.sonar.db.component.BranchDto;
import org.sonar.db.component.BranchType;
import org.sonar.db.component.ComponentDto;
import org.sonar.db.entity.EntityDto;
import org.sonar.db.portfolio.PortfolioDto;
import org.sonar.db.portfolio.PortfolioDto.SelectionMode;
import org.sonar.db.project.ProjectDto;
@@ -236,14 +237,25 @@ public class ComponentUpdater {
return branch;
}

// TODO this is wrong, should probably be done for the project and not for the component. Component has the uuid of the branch!
private void handlePermissionTemplate(DbSession dbSession, ComponentDto componentDto, @Nullable String userUuid, @Nullable String userLogin) {
permissionTemplateService.applyDefaultToNewComponent(dbSession, componentDto, userUuid);
if (componentDto.qualifier().equals(PROJECT)
&& permissionTemplateService.hasDefaultTemplateWithPermissionOnProjectCreator(dbSession, componentDto)) {
favoriteUpdater.add(dbSession, componentDto, userUuid, userLogin, false);
favoriteUpdater.add(dbSession, toProject(componentDto), userUuid, userLogin, false);
}
}

private static ProjectDto toProject(ComponentDto componentDto) {
return new ProjectDto()
.setUuid(componentDto.uuid())
.setKey(componentDto.getKey())
.setQualifier(componentDto.qualifier())
.setPrivate(componentDto.isPrivate())
.setName(componentDto.name())
.setDescription(componentDto.description());
}

private String getQualifierToDisplay(String qualifier) {
return i18n.message(Locale.getDefault(), "qualifier." + qualifier, "Project");
}

+ 41
- 70
server/sonar-webserver-webapi/src/main/java/org/sonar/server/component/ws/SuggestionsAction.java View File

@@ -26,14 +26,13 @@ import java.util.Arrays;
import java.util.Collection;
import java.util.Comparator;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import javax.annotation.CheckForNull;
import javax.annotation.Nullable;
import org.sonar.api.resources.Qualifiers;
import org.sonar.api.resources.ResourceType;
@@ -47,6 +46,7 @@ import org.sonar.core.util.stream.MoreCollectors;
import org.sonar.db.DbClient;
import org.sonar.db.DbSession;
import org.sonar.db.component.ComponentDto;
import org.sonar.db.entity.EntityDto;
import org.sonar.server.component.index.ComponentHit;
import org.sonar.server.component.index.ComponentHitsPerQualifier;
import org.sonar.server.component.index.ComponentIndex;
@@ -170,41 +170,41 @@ public class SuggestionsAction implements ComponentsWsAction {
* we are generating suggestions, by using (1) favorites and (2) recently browsed components (without searchin in Elasticsearch)
*/
private SuggestionsWsResponse loadSuggestionsWithoutSearch(int skip, int limit, Set<String> recentlyBrowsedKeys, List<String> qualifiers) {
List<ComponentDto> favoriteDtos = favoriteFinder.list();
if (favoriteDtos.isEmpty() && recentlyBrowsedKeys.isEmpty()) {
List<EntityDto> favorites = favoriteFinder.list();
if (favorites.isEmpty() && recentlyBrowsedKeys.isEmpty()) {
return newBuilder().build();
}
try (DbSession dbSession = dbClient.openSession(false)) {
Set<ComponentDto> componentDtos = new HashSet<>(favoriteDtos);
Set<EntityDto> entities = new HashSet<>(favorites);
if (!recentlyBrowsedKeys.isEmpty()) {
componentDtos.addAll(dbClient.componentDao().selectByKeys(dbSession, recentlyBrowsedKeys));
entities.addAll(dbClient.projectDao().selectEntitiesByKeys(dbSession, recentlyBrowsedKeys));
}
List<ComponentDto> authorizedComponents = userSession.keepAuthorizedComponents(USER, componentDtos);
ListMultimap<String, ComponentDto> componentsPerQualifier = authorizedComponents.stream()
.collect(MoreCollectors.index(ComponentDto::qualifier));
if (componentsPerQualifier.isEmpty()) {
List<EntityDto> authorizedEntities = userSession.keepAuthorizedEntities(USER, entities);
ListMultimap<String, EntityDto> entityPerQualifier = authorizedEntities.stream()
.collect(MoreCollectors.index(EntityDto::getQualifier));
if (entityPerQualifier.isEmpty()) {
return newBuilder().build();
}

Set<String> favoriteUuids = favoriteDtos.stream().map(ComponentDto::uuid).collect(MoreCollectors.toSet(favoriteDtos.size()));
Comparator<ComponentDto> favoriteComparator = Comparator.comparing(c -> favoriteUuids.contains(c.uuid()) ? -1 : +1);
Comparator<ComponentDto> comparator = favoriteComparator.thenComparing(ComponentDto::name);
Set<String> favoriteUuids = favorites.stream().map(EntityDto::getUuid).collect(MoreCollectors.toSet(favorites.size()));
Comparator<EntityDto> favoriteComparator = Comparator.comparing(c -> favoriteUuids.contains(c.getUuid()) ? -1 : +1);
Comparator<EntityDto> comparator = favoriteComparator.thenComparing(EntityDto::getName);

ComponentIndexResults componentsPerQualifiers = ComponentIndexResults.newBuilder().setQualifiers(
qualifiers.stream().map(q -> {
List<ComponentDto> componentsOfThisQualifier = componentsPerQualifier.get(q);
List<EntityDto> componentsOfThisQualifier = entityPerQualifier.get(q);
List<ComponentHit> hits = componentsOfThisQualifier
.stream()
.sorted(comparator)
.skip(skip)
.limit(limit)
.map(ComponentDto::uuid)
.map(EntityDto::getUuid)
.map(ComponentHit::new)
.collect(MoreCollectors.toList(limit));
int totalHits = componentsOfThisQualifier.size();
return new ComponentHitsPerQualifier(q, hits, totalHits);
})).build();
return buildResponse(recentlyBrowsedKeys, favoriteUuids, componentsPerQualifiers, dbSession, authorizedComponents, skip + limit).build();
return buildResponse(recentlyBrowsedKeys, favoriteUuids, componentsPerQualifiers, authorizedEntities, skip + limit).build();
}
}

@@ -215,8 +215,8 @@ public class SuggestionsAction implements ComponentsWsAction {
return queryBuilder.build();
}

List<ComponentDto> favorites = favoriteFinder.list();
Set<String> favoriteKeys = favorites.stream().map(ComponentDto::getKey).collect(MoreCollectors.toSet(favorites.size()));
List<EntityDto> favorites = favoriteFinder.list();
Set<String> favoriteKeys = favorites.stream().map(EntityDto::getKey).collect(MoreCollectors.toSet(favorites.size()));
SuggestionQuery.Builder queryBuilder = SuggestionQuery.builder()
.setQuery(query)
.setRecentlyBrowsedKeys(recentlyBrowsedKeys)
@@ -229,14 +229,14 @@ public class SuggestionsAction implements ComponentsWsAction {
return newBuilder().build();
}
try (DbSession dbSession = dbClient.openSession(false)) {
Set<String> componentUuids = componentsPerQualifiers.getQualifiers()
Set<String> entityUuids = componentsPerQualifiers.getQualifiers()
.map(ComponentHitsPerQualifier::getHits)
.flatMap(Collection::stream)
.map(ComponentHit::getUuid)
.collect(toSet());
List<ComponentDto> componentDtos = dbClient.componentDao().selectByUuids(dbSession, componentUuids);
Set<String> favoriteUuids = favorites.stream().map(ComponentDto::uuid).collect(MoreCollectors.toSet(favorites.size()));
SuggestionsWsResponse.Builder searchWsResponse = buildResponse(recentlyBrowsedKeys, favoriteUuids, componentsPerQualifiers, dbSession, componentDtos, skip + limit);
List<EntityDto> entities = dbClient.projectDao().selectEntitiesByUuids(dbSession, entityUuids);
Set<String> favoriteUuids = favorites.stream().map(EntityDto::getUuid).collect(MoreCollectors.toSet(favorites.size()));
SuggestionsWsResponse.Builder searchWsResponse = buildResponse(recentlyBrowsedKeys, favoriteUuids, componentsPerQualifiers, entities, skip + limit);
getWarning(query).ifPresent(searchWsResponse::setWarning);
return searchWsResponse.build();
}
@@ -269,21 +269,11 @@ public class SuggestionsAction implements ComponentsWsAction {
}

private SuggestionsWsResponse.Builder buildResponse(Set<String> recentlyBrowsedKeys, Set<String> favoriteUuids, ComponentIndexResults componentsPerQualifiers,
DbSession dbSession, List<ComponentDto> componentDtos, int coveredItems) {
List<EntityDto> entities, int coveredItems) {

Map<String, ComponentDto> componentsByUuids = componentDtos.stream()
.collect(MoreCollectors.uniqueIndex(ComponentDto::uuid));
Map<String, ComponentDto> projectsByUuids = loadProjects(dbSession, componentsByUuids.values());
return toResponse(componentsPerQualifiers, recentlyBrowsedKeys, favoriteUuids, componentsByUuids, projectsByUuids, coveredItems);
}

private Map<String, ComponentDto> loadProjects(DbSession dbSession, Collection<ComponentDto> components) {
Set<String> projectUuids = components.stream()
.filter(c -> QUALIFIERS_FOR_WHICH_TO_RETURN_PROJECT.contains(c.qualifier()))
.map(ComponentDto::branchUuid)
.collect(MoreCollectors.toSet());
return dbClient.componentDao().selectByUuids(dbSession, projectUuids).stream()
.collect(MoreCollectors.uniqueIndex(ComponentDto::uuid));
Map<String, EntityDto> entitiesByUuids = entities.stream()
.collect(MoreCollectors.uniqueIndex(EntityDto::getUuid));
return toResponse(componentsPerQualifiers, recentlyBrowsedKeys, favoriteUuids, entitiesByUuids, coveredItems);
}

private ComponentIndexResults searchInIndex(SuggestionQuery suggestionQuery) {
@@ -291,22 +281,22 @@ public class SuggestionsAction implements ComponentsWsAction {
}

private static SuggestionsWsResponse.Builder toResponse(ComponentIndexResults componentsPerQualifiers, Set<String> recentlyBrowsedKeys, Set<String> favoriteUuids,
Map<String, ComponentDto> componentsByUuids, Map<String, ComponentDto> projectsByUuids, int coveredItems) {
Map<String, EntityDto> entitiesByUuids, int coveredItems) {
if (componentsPerQualifiers.isEmpty()) {
return newBuilder();
}
return newBuilder()
.addAllResults(toCategories(componentsPerQualifiers, recentlyBrowsedKeys, favoriteUuids, componentsByUuids, projectsByUuids, coveredItems))
.addAllProjects(toProjects(projectsByUuids));
.addAllResults(toCategories(componentsPerQualifiers, recentlyBrowsedKeys, favoriteUuids, entitiesByUuids, coveredItems));
}

private static List<Category> toCategories(ComponentIndexResults componentsPerQualifiers, Set<String> recentlyBrowsedKeys, Set<String> favoriteUuids,
Map<String, ComponentDto> componentsByUuids, Map<String, ComponentDto> projectsByUuids, int coveredItems) {
Map<String, EntityDto> entitiesByUuids, int coveredItems) {
return componentsPerQualifiers.getQualifiers().map(qualifier -> {

List<Suggestion> suggestions = qualifier.getHits().stream()
.map(hit -> toSuggestion(hit, recentlyBrowsedKeys, favoriteUuids, componentsByUuids, projectsByUuids))
.filter(Objects::nonNull)
.map(hit -> toSuggestion(hit, recentlyBrowsedKeys, favoriteUuids, entitiesByUuids))
.filter(Optional::isPresent)
.map(Optional::get)
.collect(toList());

return Category.newBuilder()
@@ -321,33 +311,14 @@ public class SuggestionsAction implements ComponentsWsAction {
* @return null when the component exists in Elasticsearch but not in database. That
* occurs when failed indexing requests are been recovering.
*/
@CheckForNull
private static Suggestion toSuggestion(ComponentHit hit, Set<String> recentlyBrowsedKeys, Set<String> favoriteUuids, Map<String, ComponentDto> componentsByUuids,
Map<String, ComponentDto> projectsByUuids) {
ComponentDto result = componentsByUuids.get(hit.getUuid());
if (result == null
// SONAR-11419 this has happened in production while code does not really allow it. An inconsistency in DB may be the cause.
|| (QUALIFIERS_FOR_WHICH_TO_RETURN_PROJECT.contains(result.qualifier()) && projectsByUuids.get(result.branchUuid()) == null)) {
return null;
}
Suggestion.Builder builder = Suggestion.newBuilder()
.setKey(result.getKey())
.setName(result.name())
.setMatch(hit.getHighlightedText().orElse(HtmlEscapers.htmlEscaper().escape(result.name())))
.setIsRecentlyBrowsed(recentlyBrowsedKeys.contains(result.getKey()))
.setIsFavorite(favoriteUuids.contains(result.uuid()));
if (QUALIFIERS_FOR_WHICH_TO_RETURN_PROJECT.contains(result.qualifier())) {
builder.setProject(projectsByUuids.get(result.branchUuid()).getKey());
}
return builder.build();
}

private static List<Project> toProjects(Map<String, ComponentDto> projectsByUuids) {
return projectsByUuids.values().stream()
.map(p -> Project.newBuilder()
.setKey(p.getKey())
.setName(p.longName())
.build())
.toList();
private static Optional<Suggestion> toSuggestion(ComponentHit hit, Set<String> recentlyBrowsedKeys, Set<String> favoriteUuids, Map<String, EntityDto> entitiesByUuids) {
return Optional.ofNullable(entitiesByUuids.get(hit.getUuid()))
.map(result -> Suggestion.newBuilder()
.setKey(result.getKey())
.setName(result.getName())
.setMatch(hit.getHighlightedText().orElse(HtmlEscapers.htmlEscaper().escape(result.getName())))
.setIsRecentlyBrowsed(recentlyBrowsedKeys.contains(result.getKey()))
.setIsFavorite(favoriteUuids.contains(result.getUuid()))
.build());
}
}

+ 7
- 6
server/sonar-webserver-webapi/src/main/java/org/sonar/server/favorite/FavoriteFinder.java View File

@@ -25,13 +25,12 @@ import java.util.Set;
import java.util.stream.Collectors;
import org.sonar.db.DbClient;
import org.sonar.db.DbSession;
import org.sonar.db.component.ComponentDto;
import org.sonar.db.entity.EntityDto;
import org.sonar.db.property.PropertyDto;
import org.sonar.db.property.PropertyQuery;
import org.sonar.server.user.UserSession;

import static java.util.Collections.emptyList;
import static org.sonar.core.util.stream.MoreCollectors.toList;
import static org.sonar.server.favorite.FavoriteUpdater.PROP_FAVORITE_KEY;

public class FavoriteFinder {
@@ -46,7 +45,7 @@ public class FavoriteFinder {
/**
* @return the list of favorite components of the authenticated user. Empty list if the user is not authenticated
*/
public List<ComponentDto> list() {
public List<EntityDto> list() {
if (!userSession.isLoggedIn()) {
return emptyList();
}
@@ -58,9 +57,11 @@ public class FavoriteFinder {
.build();
Set<String> componentUuids = dbClient.propertiesDao().selectByQuery(dbQuery, dbSession).stream().map(PropertyDto::getComponentUuid).collect(Collectors.toSet());

return dbClient.componentDao().selectByUuids(dbSession, componentUuids).stream()
.sorted(Comparator.comparing(ComponentDto::name))
.collect(toList());
List<EntityDto> entities = dbClient.projectDao().selectEntitiesByUuids(dbSession, componentUuids);

return entities.stream()
.sorted(Comparator.comparing(EntityDto::getName))
.collect(Collectors.toList());
}
}
}

+ 8
- 4
server/sonar-webserver-webapi/src/main/java/org/sonar/server/favorite/ws/AddAction.java View File

@@ -28,7 +28,9 @@ import org.sonar.api.server.ws.WebService;
import org.sonar.db.DbClient;
import org.sonar.db.DbSession;
import org.sonar.db.component.ComponentDto;
import org.sonar.db.entity.EntityDto;
import org.sonar.server.component.ComponentFinder;
import org.sonar.server.exceptions.NotFoundException;
import org.sonar.server.favorite.FavoriteUpdater;
import org.sonar.server.user.UserSession;
import org.sonar.server.ws.KeyExamples;
@@ -92,14 +94,16 @@ public class AddAction implements FavoritesWsAction {
private Consumer<Request> addFavorite() {
return request -> {
try (DbSession dbSession = dbClient.openSession(false)) {
ComponentDto componentDto = componentFinder.getByKey(dbSession, request.mandatoryParam(PARAM_COMPONENT));
checkArgument(SUPPORTED_QUALIFIERS.contains(componentDto.qualifier()), "Only components with qualifiers %s are supported", SUPPORTED_QUALIFIERS_AS_STRING);
EntityDto entity = dbClient.projectDao().selectEntityByKey(dbSession, request.mandatoryParam(PARAM_COMPONENT))
.orElseThrow(() -> new NotFoundException(format("Entity with key '%s' not found", request.mandatoryParam(PARAM_COMPONENT))));

checkArgument(SUPPORTED_QUALIFIERS.contains(entity.getQualifier()), "Only components with qualifiers %s are supported", SUPPORTED_QUALIFIERS_AS_STRING);
userSession
.checkLoggedIn()
.checkComponentPermission(USER, componentDto);
.checkEntityPermission(USER, entity);
String userUuid = userSession.isLoggedIn() ? userSession.getUuid() : null;
String userLogin = userSession.isLoggedIn() ? userSession.getLogin() : null;
favoriteUpdater.add(dbSession, componentDto, userUuid, userLogin, true);
favoriteUpdater.add(dbSession, entity, userUuid, userLogin, true);
dbSession.commit();
}
};

+ 11
- 10
server/sonar-webserver-webapi/src/main/java/org/sonar/server/favorite/ws/SearchAction.java View File

@@ -28,6 +28,7 @@ import org.sonar.api.utils.Paging;
import org.sonar.api.web.UserRole;
import org.sonar.core.util.stream.MoreCollectors;
import org.sonar.db.component.ComponentDto;
import org.sonar.db.entity.EntityDto;
import org.sonar.server.favorite.FavoriteFinder;
import org.sonar.server.user.UserSession;
import org.sonarqube.ws.Common;
@@ -78,25 +79,25 @@ public class SearchAction implements FavoritesWsAction {
private SearchResults toSearchResults(SearchRequest request) {
userSession.checkLoggedIn();

List<ComponentDto> authorizedFavorites = getAuthorizedFavorites();
List<EntityDto> authorizedFavorites = getAuthorizedFavorites();
Paging paging = Paging.forPageIndex(Integer.parseInt(request.getP())).withPageSize(Integer.parseInt(request.getPs())).andTotal(authorizedFavorites.size());
List<ComponentDto> displayedFavorites = authorizedFavorites.stream()
List<EntityDto> displayedFavorites = authorizedFavorites.stream()
.skip(paging.offset())
.limit(paging.pageSize())
.collect(MoreCollectors.toList());
return new SearchResults(paging, displayedFavorites);
}

private List<ComponentDto> getAuthorizedFavorites() {
List<ComponentDto> componentDtos = favoriteFinder.list();
return userSession.keepAuthorizedComponents(UserRole.USER, componentDtos);
private List<EntityDto> getAuthorizedFavorites() {
List<EntityDto> entities = favoriteFinder.list();
return userSession.keepAuthorizedEntities(UserRole.USER, entities);
}

private static class SearchResults {
private final List<ComponentDto> favorites;
private final List<EntityDto> favorites;
private final Paging paging;

private SearchResults(Paging paging, List<ComponentDto> favorites) {
private SearchResults(Paging paging, List<EntityDto> favorites) {
this.paging = paging;
this.favorites = favorites;
}
@@ -124,12 +125,12 @@ public class SearchAction implements FavoritesWsAction {
.forEach(builder::addFavorites);
}

private static Favorite toWsFavorite(Favorite.Builder builder, ComponentDto componentDto) {
private static Favorite toWsFavorite(Favorite.Builder builder, EntityDto componentDto) {
builder
.clear()
.setKey(componentDto.getKey());
ofNullable(componentDto.name()).ifPresent(builder::setName);
ofNullable(componentDto.qualifier()).ifPresent(builder::setQualifier);
ofNullable(componentDto.getName()).ifPresent(builder::setName);
ofNullable(componentDto.getQualifier()).ifPresent(builder::setQualifier);
return builder.build();
}


+ 11
- 5
server/sonar-webserver-webapi/src/main/java/org/sonar/server/setting/ws/ResetAction.java View File

@@ -35,10 +35,13 @@ import org.sonar.core.util.stream.MoreCollectors;
import org.sonar.db.DbClient;
import org.sonar.db.DbSession;
import org.sonar.db.component.ComponentDto;
import org.sonar.db.entity.EntityDto;
import org.sonar.server.component.ComponentFinder;
import org.sonar.server.exceptions.NotFoundException;
import org.sonar.server.setting.ws.SettingValidations.SettingData;
import org.sonar.server.user.UserSession;

import static java.lang.String.format;
import static java.util.Collections.emptyList;
import static org.sonar.server.setting.ws.SettingsWsParameters.PARAM_BRANCH;
import static org.sonar.server.setting.ws.SettingsWsParameters.PARAM_COMPONENT;
@@ -107,7 +110,7 @@ public class ResetAction implements SettingsWsAction {
public void handle(Request request, Response response) throws Exception {
try (DbSession dbSession = dbClient.openSession(false)) {
ResetRequest resetRequest = toWsRequest(request);
Optional<ComponentDto> component = getComponent(dbSession, resetRequest);
Optional<EntityDto> component = getComponent(dbSession, resetRequest);
checkPermissions(component);
resetRequest.getKeys().forEach(key -> {
SettingsWsSupport.validateKey(key);
@@ -144,17 +147,20 @@ public class ResetAction implements SettingsWsAction {
.setPullRequest(request.param(PARAM_PULL_REQUEST));
}

private Optional<ComponentDto> getComponent(DbSession dbSession, ResetRequest request) {
private Optional<EntityDto> getComponent(DbSession dbSession, ResetRequest request) {
String componentKey = request.getComponent();
if (componentKey == null) {
return Optional.empty();
}
return Optional.of(componentFinder.getByKeyAndOptionalBranchOrPullRequest(dbSession, componentKey, request.getBranch(), request.getPullRequest()));

// TODO this WS should support branches and PRs?
return Optional.of(dbClient.projectDao().selectEntityByKey(dbSession, componentKey)
.orElseThrow(() -> new NotFoundException(format("Component key '%s' not found", componentKey))));
}

private void checkPermissions(Optional<ComponentDto> component) {
private void checkPermissions(Optional<EntityDto> component) {
if (component.isPresent()) {
userSession.checkComponentPermission(UserRole.ADMIN, component.get());
userSession.checkEntityPermission(UserRole.ADMIN, component.get());
} else {
userSession.checkIsSystemAdministrator();
}

+ 23
- 20
server/sonar-webserver-webapi/src/main/java/org/sonar/server/setting/ws/SetAction.java View File

@@ -48,6 +48,7 @@ import org.sonar.api.web.UserRole;
import org.sonar.db.DbClient;
import org.sonar.db.DbSession;
import org.sonar.db.component.ComponentDto;
import org.sonar.db.entity.EntityDto;
import org.sonar.db.property.PropertyDto;
import org.sonar.scanner.protocol.GsonHelper;
import org.sonar.server.component.ComponentFinder;
@@ -57,6 +58,7 @@ import org.sonar.server.setting.ws.SettingValidations.SettingData;
import org.sonar.server.user.UserSession;

import static com.google.common.base.Preconditions.checkArgument;
import static java.lang.String.format;
import static org.sonar.server.exceptions.BadRequestException.checkRequest;
import static org.sonar.server.setting.ws.SettingsWsParameters.PARAM_COMPONENT;
import static org.sonar.server.setting.ws.SettingsWsParameters.PARAM_FIELD_VALUES;
@@ -103,9 +105,9 @@ public class SetAction implements SettingsWsAction {
PARAM_VALUE, PARAM_VALUES)
.setSince("6.1")
.setChangelog(
new Change("10.1", String.format("The use of module keys in parameter '%s' is removed", PARAM_COMPONENT)),
new Change("10.1", format("The use of module keys in parameter '%s' is removed", PARAM_COMPONENT)),
new Change("8.8", "Deprecated parameter 'componentKey' has been removed"),
new Change("7.6", String.format("The use of module keys in parameter '%s' is deprecated", PARAM_COMPONENT)),
new Change("7.6", format("The use of module keys in parameter '%s' is deprecated", PARAM_COMPONENT)),
new Change("7.1", "The settings defined in conf/sonar.properties are read-only and can't be changed"))
.setPost(true)
.setHandler(this);
@@ -144,10 +146,10 @@ public class SetAction implements SettingsWsAction {
}

private void doHandle(DbSession dbSession, SetRequest request) {
Optional<ComponentDto> component = searchComponent(dbSession, request);
String projectKey = component.isPresent() ? component.get().getKey() : null;
String projectName = component.isPresent() ? component.get().name() : null;
String qualifier = component.isPresent() ? component.get().qualifier() : null;
Optional<EntityDto> component = searchComponent(dbSession, request);
String projectKey = component.map(EntityDto::getKey).orElse(null);
String projectName = component.map(EntityDto::getName).orElse(null);
String qualifier = component.map(EntityDto::getQualifier).orElse(null);
checkPermissions(component);

PropertyDefinition definition = propertyDefinitions.get(request.getKey());
@@ -172,16 +174,16 @@ public class SetAction implements SettingsWsAction {
}
}

private String doHandlePropertySet(DbSession dbSession, SetRequest request, @Nullable PropertyDefinition definition, Optional<ComponentDto> component) {
private String doHandlePropertySet(DbSession dbSession, SetRequest request, @Nullable PropertyDefinition definition, Optional<EntityDto> component) {
validatePropertySet(request, definition);

int[] fieldIds = IntStream.rangeClosed(1, request.getFieldValues().size()).toArray();
String inlinedFieldKeys = IntStream.of(fieldIds).mapToObj(String::valueOf).collect(COMMA_JOINER);
String key = persistedKey(request);
String componentUuid = component.isPresent() ? component.get().uuid() : null;
String componentUuid = component.isPresent() ? component.get().getUuid() : null;
String componentKey = component.isPresent() ? component.get().getKey() : null;
String componentName = component.isPresent() ? component.get().name() : null;
String qualifier = component.isPresent() ? component.get().qualifier() : null;
String componentName = component.isPresent() ? component.get().getName() : null;
String qualifier = component.isPresent() ? component.get().getQualifier() : null;

deleteSettings(dbSession, component, key);
dbClient.propertiesDao().saveProperty(dbSession, new PropertyDto().setKey(key).setValue(inlinedFieldKeys)
@@ -197,7 +199,7 @@ public class SetAction implements SettingsWsAction {
return inlinedFieldKeys;
}

private void deleteSettings(DbSession dbSession, Optional<ComponentDto> component, String key) {
private void deleteSettings(DbSession dbSession, Optional<EntityDto> component, String key) {
if (component.isPresent()) {
settingsUpdater.deleteComponentSettings(dbSession, component.get(), key);
} else {
@@ -205,7 +207,7 @@ public class SetAction implements SettingsWsAction {
}
}

private void commonChecks(SetRequest request, Optional<ComponentDto> component) {
private void commonChecks(SetRequest request, Optional<EntityDto> component) {
checkValueIsSet(request);
String settingKey = request.getKey();
SettingData settingData = new SettingData(settingKey, valuesFromRequest(request), component.orElse(null));
@@ -284,9 +286,9 @@ public class SetAction implements SettingsWsAction {
: request.getValue();
}

private void checkPermissions(Optional<ComponentDto> component) {
private void checkPermissions(Optional<EntityDto> component) {
if (component.isPresent()) {
userSession.checkComponentPermission(UserRole.ADMIN, component.get());
userSession.checkEntityPermission(UserRole.ADMIN, component.get());
} else {
userSession.checkIsSystemAdministrator();
}
@@ -311,19 +313,20 @@ public class SetAction implements SettingsWsAction {
try {
return gson.fromJson(json, type);
} catch (JsonSyntaxException e) {
throw BadRequestException.create(String.format("JSON '%s' does not respect expected format for setting '%s'. Ex: {\"field1\":\"value1\", \"field2\":\"value2\"}", json, key));
throw BadRequestException.create(format("JSON '%s' does not respect expected format for setting '%s'. Ex: {\"field1\":\"value1\", \"field2\":\"value2\"}", json, key));
}
}

private Optional<ComponentDto> searchComponent(DbSession dbSession, SetRequest request) {
private Optional<EntityDto> searchComponent(DbSession dbSession, SetRequest request) {
String componentKey = request.getComponent();
if (componentKey == null) {
return Optional.empty();
}
return Optional.of(componentFinder.getByKey(dbSession, componentKey));
return Optional.of(dbClient.projectDao().selectEntityByKey(dbSession, componentKey)
.orElseThrow(() -> new IllegalArgumentException(format("Component '%s' not found", componentKey))));
}

private PropertyDto toProperty(SetRequest request, Optional<ComponentDto> component) {
private PropertyDto toProperty(SetRequest request, Optional<EntityDto> entity) {
String key = persistedKey(request);
String value = persistedValue(request);

@@ -331,8 +334,8 @@ public class SetAction implements SettingsWsAction {
.setKey(key)
.setValue(value);

if (component.isPresent()) {
property.setComponentUuid(component.get().uuid());
if (entity.isPresent()) {
property.setComponentUuid(entity.get().getUuid());
}

return property;

+ 12
- 17
server/sonar-webserver-webapi/src/main/java/org/sonar/server/setting/ws/SettingValidations.java View File

@@ -40,11 +40,10 @@ import org.sonar.api.PropertyType;
import org.sonar.api.config.PropertyDefinition;
import org.sonar.api.config.PropertyDefinitions;
import org.sonar.api.resources.Qualifiers;
import org.sonar.api.resources.Scopes;
import org.sonar.core.i18n.I18n;
import org.sonar.db.DbClient;
import org.sonar.db.DbSession;
import org.sonar.db.component.ComponentDto;
import org.sonar.db.entity.EntityDto;
import org.sonar.db.metric.MetricDto;
import org.sonar.db.user.UserDto;
import org.sonar.server.exceptions.BadRequestException;
@@ -55,7 +54,7 @@ import static java.util.Objects.requireNonNull;
import static org.sonar.server.exceptions.BadRequestException.checkRequest;

public class SettingValidations {
private static final Collection<String> SECURITY_JSON_PROPERTIES = asList(
private static final Collection<String> SECURITY_JSON_PROPERTIES = List.of(
"sonar.security.config.javasecurity",
"sonar.security.config.phpsecurity",
"sonar.security.config.pythonsecurity",
@@ -76,32 +75,29 @@ public class SettingValidations {
public Consumer<SettingData> scope() {
return data -> {
PropertyDefinition definition = definitions.get(data.key);
checkRequest(data.component != null || definition == null || definition.global() || isGlobal(definition),
checkRequest(data.entity != null || definition == null || definition.global() || isGlobal(definition),
"Setting '%s' cannot be global", data.key);
};
}

public Consumer<SettingData> qualifier() {
return data -> {
String qualifier = data.component == null ? "" : data.component.qualifier();
String qualifier = data.entity == null ? "" : data.entity.getQualifier();
PropertyDefinition definition = definitions.get(data.key);
checkRequest(checkComponentScopeAndQualifier(data, definition),
checkRequest(checkComponentQualifier(data, definition),
"Setting '%s' cannot be set on a %s", data.key, i18n.message(Locale.ENGLISH, "qualifier." + qualifier, null));
};
}

private static boolean checkComponentScopeAndQualifier(SettingData data, @Nullable PropertyDefinition definition) {
ComponentDto component = data.component;
private static boolean checkComponentQualifier(SettingData data, @Nullable PropertyDefinition definition) {
EntityDto component = data.entity;
if (component == null) {
return true;
}
if (!Scopes.PROJECT.equals(component.scope())) {
return false;
}
if (definition == null) {
return SUPPORTED_QUALIFIERS.contains(component.qualifier());
return SUPPORTED_QUALIFIERS.contains(component.getQualifier());
}
return definition.qualifiers().contains(component.qualifier());
return definition.qualifiers().contains(component.getQualifier());
}

public Consumer<SettingData> valueType() {
@@ -116,12 +112,12 @@ public class SettingValidations {
private final String key;
private final List<String> values;
@CheckForNull
private final ComponentDto component;
private final EntityDto entity;

SettingData(String key, List<String> values, @Nullable ComponentDto component) {
SettingData(String key, List<String> values, @Nullable EntityDto entity) {
this.key = requireNonNull(key);
this.values = requireNonNull(values);
this.component = component;
this.entity = entity;
}
}

@@ -195,7 +191,6 @@ public class SettingValidations {
SchemaLoader.load(jsonSchema).validate(jsonSubject);
}
}

}
}
}

+ 21
- 19
server/sonar-webserver-webapi/src/main/java/org/sonar/server/setting/ws/SettingsUpdater.java View File

@@ -22,12 +22,14 @@ package org.sonar.server.setting.ws;
import java.util.List;
import java.util.Optional;
import java.util.Set;
import javax.annotation.Nullable;
import org.sonar.api.PropertyType;
import org.sonar.api.config.PropertyDefinition;
import org.sonar.api.config.PropertyDefinitions;
import org.sonar.db.DbClient;
import org.sonar.db.DbSession;
import org.sonar.db.component.ComponentDto;
import org.sonar.db.entity.EntityDto;
import org.sonar.db.property.PropertyDto;

import static com.google.common.base.Preconditions.checkArgument;
@@ -50,54 +52,54 @@ public class SettingsUpdater {

public void deleteGlobalSettings(DbSession dbSession, List<String> settingKeys) {
checkArgument(!settingKeys.isEmpty(), "At least one setting key is required");
settingKeys.forEach(key -> delete(dbSession, key, Optional.empty()));
settingKeys.forEach(key -> delete(dbSession, key, null));
}

public void deleteComponentSettings(DbSession dbSession, ComponentDto componentDto, String... settingKeys) {
deleteComponentSettings(dbSession, componentDto, asList(settingKeys));
public void deleteComponentSettings(DbSession dbSession, EntityDto entity, String... settingKeys) {
deleteComponentSettings(dbSession, entity, asList(settingKeys));
}

public void deleteComponentSettings(DbSession dbSession, ComponentDto componentDto, List<String> settingKeys) {
public void deleteComponentSettings(DbSession dbSession, EntityDto entity, List<String> settingKeys) {
checkArgument(!settingKeys.isEmpty(), "At least one setting key is required");
for (String propertyKey : settingKeys) {
delete(dbSession, propertyKey, Optional.of(componentDto));
delete(dbSession, propertyKey, entity);
}
}

private void delete(DbSession dbSession, String settingKey, Optional<ComponentDto> componentDto) {
private void delete(DbSession dbSession, String settingKey, @Nullable EntityDto entity) {
PropertyDefinition definition = definitions.get(settingKey);
if (definition == null || !definition.type().equals(PropertyType.PROPERTY_SET)) {
deleteSetting(dbSession, settingKey, componentDto);
deleteSetting(dbSession, settingKey, entity);
} else {
deletePropertySet(dbSession, settingKey, definition, componentDto);
deletePropertySet(dbSession, settingKey, definition, entity);
}
}

private void deleteSetting(DbSession dbSession, String settingKey, Optional<ComponentDto> componentDto) {
if (componentDto.isPresent()) {
dbClient.propertiesDao().deleteProjectProperty(dbSession, settingKey, componentDto.get().uuid(), componentDto.get().getKey(),
componentDto.get().name(), componentDto.get().qualifier());
private void deleteSetting(DbSession dbSession, String settingKey, @Nullable EntityDto entity) {
if (entity != null) {
dbClient.propertiesDao().deleteProjectProperty(dbSession, settingKey, entity.getUuid(), entity.getKey(),
entity.getName(), entity.getQualifier());
} else {
dbClient.propertiesDao().deleteGlobalProperty(settingKey, dbSession);
}
}

private void deletePropertySet(DbSession dbSession, String settingKey, PropertyDefinition definition, Optional<ComponentDto> componentDto) {
Optional<PropertyDto> propertyDto = selectPropertyDto(dbSession, settingKey, componentDto);
private void deletePropertySet(DbSession dbSession, String settingKey, PropertyDefinition definition, @Nullable EntityDto entity) {
Optional<PropertyDto> propertyDto = selectPropertyDto(dbSession, settingKey, entity);
if (!propertyDto.isPresent()) {
// Setting doesn't exist, nothing to do
return;
}
Set<String> settingSetKeys = extractPropertySetKeys(propertyDto.get(), definition);
for (String key : settingSetKeys) {
deleteSetting(dbSession, key, componentDto);
deleteSetting(dbSession, key, entity);
}
deleteSetting(dbSession, settingKey, componentDto);
deleteSetting(dbSession, settingKey, entity);
}

private Optional<PropertyDto> selectPropertyDto(DbSession dbSession, String settingKey, Optional<ComponentDto> componentDto) {
if (componentDto.isPresent()) {
return Optional.ofNullable(dbClient.propertiesDao().selectProjectProperty(dbSession, componentDto.get().uuid(), settingKey));
private Optional<PropertyDto> selectPropertyDto(DbSession dbSession, String settingKey, @Nullable EntityDto entity) {
if (entity != null) {
return Optional.ofNullable(dbClient.propertiesDao().selectProjectProperty(dbSession, entity.getUuid(), settingKey));
} else {
return Optional.ofNullable(dbClient.propertiesDao().selectGlobalProperty(dbSession, settingKey));
}

Loading…
Cancel
Save