Browse Source

SONAR-10597 Update MANUAL_MEASURES#USER_LOGIN to USER_UUID

* SONAR-10597 Update MANUAL_MEASURES#USER_LOGIN to USER_UUD in DB

* SONAR-10597 Update api/custom_measures/create to use user uuid

* SONAR-10597 Update api/custom_measures/update to use user uuid

* SONAR-10597 Remove CustomMeasureDao#selectOrFail

* SONAR-10597 Update api/custom_measures/search to use user uuid

* SONAR-10597 Clean up no more needed code

* SONAR-10597 Add ITs to check manual measure after login update
tags/7.5
Julien Lancelot 6 years ago
parent
commit
2e0c39c865
29 changed files with 1079 additions and 863 deletions
  1. 1
    1
      server/sonar-db-core/src/main/resources/org/sonar/db/version/schema-h2.ddl
  2. 0
    9
      server/sonar-db-dao/src/main/java/org/sonar/db/measure/custom/CustomMeasureDao.java
  3. 7
    6
      server/sonar-db-dao/src/main/java/org/sonar/db/measure/custom/CustomMeasureDto.java
  4. 0
    7
      server/sonar-db-dao/src/main/java/org/sonar/db/metric/MetricDao.java
  5. 0
    9
      server/sonar-db-dao/src/main/java/org/sonar/db/user/UserDao.java
  6. 4
    4
      server/sonar-db-dao/src/main/resources/org/sonar/db/measure/custom/CustomMeasureMapper.xml
  7. 17
    3
      server/sonar-db-dao/src/test/java/org/sonar/db/measure/MeasureDbTester.java
  8. 77
    63
      server/sonar-db-dao/src/test/java/org/sonar/db/measure/custom/CustomMeasureDaoTest.java
  9. 1
    1
      server/sonar-db-dao/src/test/java/org/sonar/db/measure/custom/CustomMeasureTesting.java
  10. 0
    16
      server/sonar-db-dao/src/test/java/org/sonar/db/metric/MetricDaoTest.java
  11. 3
    18
      server/sonar-db-dao/src/test/java/org/sonar/db/user/UserDaoTest.java
  12. 0
    14
      server/sonar-db-dao/src/test/resources/org/sonar/db/measure/custom/CustomMeasureDaoTest/select_by_metric_key_and_text_value.xml
  13. 1
    0
      server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v72/DbVersion72.java
  14. 48
    0
      server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v72/RenameUserLoginToUserUuidOnTableManualMeasures.java
  15. 1
    1
      server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v72/DbVersion72Test.java
  16. 57
    0
      server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v72/RenameUserLoginToUserUuidOnTableManualMeasuresTest.java
  17. 12
    0
      server/sonar-db-migration/src/test/resources/org/sonar/server/platform/db/migration/version/v72/RenameUserLoginToUserUuidOnTableManualMeasuresTest/manual_measures.sql
  18. 16
    9
      server/sonar-server/src/main/java/org/sonar/server/measure/custom/ws/CreateAction.java
  19. 2
    13
      server/sonar-server/src/main/java/org/sonar/server/measure/custom/ws/CustomMeasureJsonWriter.java
  20. 4
    1
      server/sonar-server/src/main/java/org/sonar/server/measure/custom/ws/DeleteAction.java
  21. 23
    29
      server/sonar-server/src/main/java/org/sonar/server/measure/custom/ws/SearchAction.java
  22. 12
    4
      server/sonar-server/src/main/java/org/sonar/server/measure/custom/ws/UpdateAction.java
  23. 274
    234
      server/sonar-server/src/test/java/org/sonar/server/measure/custom/ws/CreateActionTest.java
  24. 2
    2
      server/sonar-server/src/test/java/org/sonar/server/measure/custom/ws/DeleteActionTest.java
  25. 309
    197
      server/sonar-server/src/test/java/org/sonar/server/measure/custom/ws/SearchActionTest.java
  26. 140
    201
      server/sonar-server/src/test/java/org/sonar/server/measure/custom/ws/UpdateActionTest.java
  27. 4
    4
      server/sonar-server/src/test/java/org/sonar/server/user/ws/ChangePasswordActionTest.java
  28. 0
    17
      server/sonar-server/src/test/resources/org/sonar/server/measure/custom/ws/CreateActionTest/custom-measure.json
  29. 64
    0
      tests/src/test/java/org/sonarqube/tests/user/SonarCloudUpdateLoginDuringAuthenticationTest.java

+ 1
- 1
server/sonar-db-core/src/main/resources/org/sonar/db/version/schema-h2.ddl View File

@@ -404,7 +404,7 @@ CREATE TABLE "MANUAL_MEASURES" (
"COMPONENT_UUID" VARCHAR(50) NOT NULL,
"VALUE" DOUBLE,
"TEXT_VALUE" VARCHAR(4000),
"USER_LOGIN" VARCHAR(255),
"USER_UUID" VARCHAR(255),
"DESCRIPTION" VARCHAR(4000),
"CREATED_AT" BIGINT,
"UPDATED_AT" BIGINT

+ 0
- 9
server/sonar-db-dao/src/main/java/org/sonar/db/measure/custom/CustomMeasureDao.java View File

@@ -25,7 +25,6 @@ import org.apache.ibatis.session.RowBounds;
import org.sonar.db.Dao;
import org.sonar.db.DatabaseUtils;
import org.sonar.db.DbSession;
import org.sonar.db.RowNotFoundException;

public class CustomMeasureDao implements Dao {
public void insert(DbSession session, CustomMeasureDto customMeasureDto) {
@@ -49,14 +48,6 @@ public class CustomMeasureDao implements Dao {
return mapper(session).selectById(id);
}

public CustomMeasureDto selectOrFail(DbSession session, long id) {
CustomMeasureDto customMeasure = selectById(session, id);
if (customMeasure == null) {
throw new RowNotFoundException(String.format("Custom measure '%d' not found.", id));
}
return customMeasure;
}

public List<CustomMeasureDto> selectByMetricId(DbSession session, int metricId) {
return mapper(session).selectByMetricId(metricId);
}

+ 7
- 6
server/sonar-db-dao/src/main/java/org/sonar/db/measure/custom/CustomMeasureDto.java View File

@@ -29,26 +29,27 @@ public class CustomMeasureDto {
private String componentUuid;
private double value;
private String textValue;
private String userLogin;
private String userUuid;
private String description;
private long createdAt;
private long updatedAt;

@CheckForNull
public String getDescription() {
return description;
}

public CustomMeasureDto setDescription(String description) {
public CustomMeasureDto setDescription(@Nullable String description) {
this.description = description;
return this;
}

public String getUserLogin() {
return userLogin;
public String getUserUuid() {
return userUuid;
}

public CustomMeasureDto setUserLogin(String userLogin) {
this.userLogin = userLogin;
public CustomMeasureDto setUserUuid(String userUuid) {
this.userUuid = userUuid;
return this;
}


+ 0
- 7
server/sonar-db-dao/src/main/java/org/sonar/db/metric/MetricDao.java View File

@@ -141,11 +141,4 @@ public class MetricDao implements Dao {
return mapper(session).selectById(id);
}

public MetricDto selectOrFailById(DbSession session, long id) {
MetricDto metric = mapper(session).selectById(id);
if (metric == null) {
throw new RowNotFoundException(String.format("Metric id '%d' not found", id));
}
return metric;
}
}

+ 0
- 9
server/sonar-db-dao/src/main/java/org/sonar/db/user/UserDao.java View File

@@ -35,7 +35,6 @@ import org.sonar.api.utils.System2;
import org.sonar.core.util.UuidFactory;
import org.sonar.db.Dao;
import org.sonar.db.DbSession;
import org.sonar.db.RowNotFoundException;
import org.sonar.db.component.ComponentDto;
import org.sonar.db.organization.OrganizationDto;

@@ -153,14 +152,6 @@ public class UserDao implements Dao {
return mapper(session).selectByLogin(login);
}

public UserDto selectOrFailByLogin(DbSession session, String login) {
UserDto user = selectByLogin(session, login);
if (user == null) {
throw new RowNotFoundException(String.format("User with login '%s' has not been found", login));
}
return user;
}

public List<UserDto> selectByScmAccountOrLoginOrEmail(DbSession session, String scmAccountOrLoginOrEmail) {
String like = new StringBuilder().append("%")
.append(UserDto.SCM_ACCOUNTS_SEPARATOR).append(scmAccountOrLoginOrEmail)

+ 4
- 4
server/sonar-db-dao/src/main/resources/org/sonar/db/measure/custom/CustomMeasureMapper.xml View File

@@ -8,7 +8,7 @@
m.component_uuid as componentUuid,
m.value,
m.text_value as textValue,
m.user_login as userLogin,
m.user_uuid as userUuid,
m.description,
m.created_at as createdAt,
m.updated_at as updatedAt
@@ -47,11 +47,11 @@

<insert id="insert" parameterType="CustomMeasure" useGeneratedKeys="true" keyColumn="id" keyProperty="id">
INSERT INTO manual_measures (
metric_id, component_uuid, value, text_value, user_login, description, created_at, updated_at
metric_id, component_uuid, value, text_value, user_uuid, description, created_at, updated_at
)
VALUES (
#{metricId, jdbcType=INTEGER}, #{componentUuid, jdbcType=VARCHAR},
#{value, jdbcType=DOUBLE}, #{textValue, jdbcType=VARCHAR}, #{userLogin, jdbcType=VARCHAR},
#{value, jdbcType=DOUBLE}, #{textValue, jdbcType=VARCHAR}, #{userUuid, jdbcType=VARCHAR},
#{description, jdbcType=VARCHAR}, #{createdAt, jdbcType=BIGINT}, #{updatedAt, jdbcType=BIGINT}
)
</insert>
@@ -61,7 +61,7 @@
set value = #{value, jdbcType=DOUBLE},
text_value = #{textValue, jdbcType=VARCHAR},
description = #{description, jdbcType=VARCHAR},
user_login = #{userLogin, jdbcType=VARCHAR},
user_uuid = #{userUuid, jdbcType=VARCHAR},
updated_at = #{updatedAt, jdbcType=BIGINT}
where id = #{id}
</update>

+ 17
- 3
server/sonar-db-dao/src/test/java/org/sonar/db/measure/MeasureDbTester.java View File

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

import com.google.common.base.Preconditions;
import java.util.Arrays;
import java.util.function.Consumer;
import org.sonar.db.DbClient;
@@ -26,10 +27,13 @@ import org.sonar.db.DbSession;
import org.sonar.db.DbTester;
import org.sonar.db.component.ComponentDto;
import org.sonar.db.component.SnapshotDto;
import org.sonar.db.measure.custom.CustomMeasureDto;
import org.sonar.db.metric.MetricDto;
import org.sonar.db.user.UserDto;

import static org.sonar.db.measure.MeasureTesting.newLiveMeasure;
import static org.sonar.db.measure.MeasureTesting.newMeasureDto;
import static org.sonar.db.measure.custom.CustomMeasureTesting.newCustomMeasureDto;
import static org.sonar.db.metric.MetricTesting.newMetricDto;

public class MeasureDbTester {
@@ -42,7 +46,7 @@ public class MeasureDbTester {
}

@SafeVarargs
public final MeasureDto insertMeasure(ComponentDto component, SnapshotDto analysis, MetricDto metricDto, Consumer<MeasureDto>... consumers){
public final MeasureDto insertMeasure(ComponentDto component, SnapshotDto analysis, MetricDto metricDto, Consumer<MeasureDto>... consumers) {
MeasureDto measureDto = newMeasureDto(metricDto, component, analysis);
Arrays.stream(consumers).forEach(c -> c.accept(measureDto));
dbClient.measureDao().insert(dbSession, measureDto);
@@ -51,7 +55,7 @@ public class MeasureDbTester {
}

@SafeVarargs
public final LiveMeasureDto insertLiveMeasure(ComponentDto component, MetricDto metric, Consumer<LiveMeasureDto>... consumers){
public final LiveMeasureDto insertLiveMeasure(ComponentDto component, MetricDto metric, Consumer<LiveMeasureDto>... consumers) {
LiveMeasureDto dto = newLiveMeasure(component, metric);
Arrays.stream(consumers).forEach(c -> c.accept(dto));
dbClient.liveMeasureDao().insert(dbSession, dto);
@@ -60,7 +64,17 @@ public class MeasureDbTester {
}

@SafeVarargs
public final MetricDto insertMetric(Consumer<MetricDto>... consumers){
public final CustomMeasureDto insertCustomMeasure(UserDto user, ComponentDto component, MetricDto metricDto, Consumer<CustomMeasureDto>... consumers) {
Preconditions.checkArgument(metricDto.isUserManaged(),"Custom measure must be created from a custom metric");
CustomMeasureDto dto = newCustomMeasureDto().setUserUuid(user.getUuid()).setComponentUuid(component.uuid()).setMetricId(metricDto.getId());
Arrays.stream(consumers).forEach(c -> c.accept(dto));
dbClient.customMeasureDao().insert(dbSession, dto);
dbSession.commit();
return dto;
}

@SafeVarargs
public final MetricDto insertMetric(Consumer<MetricDto>... consumers) {
MetricDto metricDto = newMetricDto();
Arrays.stream(consumers).forEach(c -> c.accept(metricDto));
dbClient.metricDao().insert(dbSession, metricDto);

+ 77
- 63
server/sonar-db-dao/src/test/java/org/sonar/db/measure/custom/CustomMeasureDaoTest.java View File

@@ -19,49 +19,48 @@
*/
package org.sonar.db.measure.custom;

import java.util.Arrays;
import java.util.List;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
import org.sonar.api.utils.System2;
import org.sonar.db.DbSession;
import org.sonar.db.DbTester;
import org.sonar.db.RowNotFoundException;
import org.sonar.db.component.ComponentDto;
import org.sonar.db.metric.MetricDto;
import org.sonar.db.user.UserDto;

import static java.util.Collections.singletonList;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.offset;
import static org.assertj.core.api.Assertions.tuple;
import static org.sonar.db.measure.custom.CustomMeasureTesting.newCustomMeasureDto;


public class CustomMeasureDaoTest {
@Rule
public DbTester db = DbTester.create(System2.INSTANCE);
@Rule
public ExpectedException expectedException = ExpectedException.none();

CustomMeasureDao underTest;
DbSession session;
private DbSession session = db.getSession();

@Before
public void setUp() {
session = db.getSession();
underTest = new CustomMeasureDao();
}
private CustomMeasureDao underTest = new CustomMeasureDao();

@Test
public void insert() {
CustomMeasureDto measure = newCustomMeasureDto();
UserDto user = db.users().insertUser();
ComponentDto project = db.components().insertPrivateProject();
MetricDto metric = db.measures().insertMetric(m -> m.setUserManaged(true));
CustomMeasureDto measure = newCustomMeasureDto()
.setComponentUuid(project.uuid())
.setMetricId(metric.getId())
.setUserUuid(user.getUuid());

underTest.insert(session, measure);

CustomMeasureDto result = underTest.selectOrFail(session, measure.getId());
CustomMeasureDto result = underTest.selectById(session, measure.getId());
assertThat(result.getId()).isEqualTo(measure.getId());
assertThat(result.getMetricId()).isEqualTo(measure.getMetricId());
assertThat(result.getComponentUuid()).isEqualTo(measure.getComponentUuid());
assertThat(result.getMetricId()).isEqualTo(metric.getId());
assertThat(result.getComponentUuid()).isEqualTo(project.uuid());
assertThat(result.getUserUuid()).isEqualTo(user.getUuid());
assertThat(result.getDescription()).isEqualTo(measure.getDescription());
assertThat(result.getUserLogin()).isEqualTo(measure.getUserLogin());
assertThat(result.getTextValue()).isEqualTo(measure.getTextValue());
assertThat(result.getValue()).isCloseTo(measure.getValue(), offset(0.001d));
assertThat(result.getCreatedAt()).isEqualTo(measure.getCreatedAt());
@@ -70,60 +69,76 @@ public class CustomMeasureDaoTest {

@Test
public void delete_by_metric_id() {
CustomMeasureDto measure = newCustomMeasureDto();
underTest.insert(session, measure);
assertThat(underTest.selectById(session, measure.getId())).isNotNull();
UserDto user = db.users().insertUser();
ComponentDto project = db.components().insertPrivateProject();
MetricDto metric = db.measures().insertMetric(m -> m.setUserManaged(true));
CustomMeasureDto measure = db.measures().insertCustomMeasure(user, project, metric);

underTest.deleteByMetricIds(session, Arrays.asList(measure.getMetricId()));
underTest.deleteByMetricIds(session, singletonList(measure.getMetricId()));

assertThat(underTest.selectById(session, measure.getId())).isNull();
}

@Test
public void update() {
CustomMeasureDto measure = newCustomMeasureDto().setDescription("old-description");
underTest.insert(session, measure);
measure.setDescription("new-description");
UserDto user = db.users().insertUser();
ComponentDto project = db.components().insertPrivateProject();
MetricDto metric = db.measures().insertMetric(m -> m.setUserManaged(true));
CustomMeasureDto measure = db.measures().insertCustomMeasure(user, project, metric, m -> m.setDescription("old-description"));

underTest.update(session, measure);
underTest.update(session, measure.setDescription("new-description"));

assertThat(underTest.selectById(session, measure.getId()).getDescription()).isEqualTo("new-description");
}

@Test
public void delete() {
CustomMeasureDto measure = newCustomMeasureDto();
underTest.insert(session, measure);
UserDto user = db.users().insertUser();
ComponentDto project = db.components().insertPrivateProject();
MetricDto metric = db.measures().insertMetric(m -> m.setUserManaged(true));
CustomMeasureDto measure = db.measures().insertCustomMeasure(user, project, metric);

underTest.delete(session, measure.getId());

assertThat(underTest.selectById(session, measure.getId())).isNull();
}

@Test
public void select_by_component_uuid() {
underTest.insert(session, newCustomMeasureDto().setComponentUuid("u1"));
underTest.insert(session, newCustomMeasureDto().setComponentUuid("u1"));
underTest.insert(session, newCustomMeasureDto().setComponentUuid("u2"));
session.commit();

List<CustomMeasureDto> result = underTest.selectByComponentUuid(session, "u1");

assertThat(result).hasSize(2);
assertThat(result).extracting("componentUuid").containsOnly("u1");
assertThat(underTest.countByComponentUuid(session, "u1")).isEqualTo(2);
UserDto user = db.users().insertUser();
MetricDto metric = db.measures().insertMetric(m -> m.setUserManaged(true));
ComponentDto project1 = db.components().insertPrivateProject();
CustomMeasureDto measure1 = db.measures().insertCustomMeasure(user, project1, metric);
CustomMeasureDto measure2 = db.measures().insertCustomMeasure(user, project1, metric);
ComponentDto project2 = db.components().insertPrivateProject();
CustomMeasureDto measure3 = db.measures().insertCustomMeasure(user, project2, metric);

assertThat(underTest.selectByComponentUuid(session, project1.uuid()))
.extracting(CustomMeasureDto::getId, CustomMeasureDto::getComponentUuid)
.containsOnly(
tuple(measure1.getId(), project1.uuid()),
tuple(measure2.getId(), project1.uuid()))
.doesNotContain(tuple(measure3.getId(), project2.uuid()));

assertThat(underTest.countByComponentUuid(session, project1.uuid())).isEqualTo(2);
}

@Test
public void select_by_component_uuid_with_options() {
underTest.insert(session, newCustomMeasureDto().setComponentUuid("u1"));
underTest.insert(session, newCustomMeasureDto().setComponentUuid("u1"));
underTest.insert(session, newCustomMeasureDto().setComponentUuid("u2"));
session.commit();

List<CustomMeasureDto> result = underTest.selectByComponentUuid(session, "u1", 0, 100);

assertThat(result).hasSize(2);
assertThat(result).extracting("componentUuid").containsOnly("u1");
UserDto user = db.users().insertUser();
MetricDto metric = db.measures().insertMetric(m -> m.setUserManaged(true));
ComponentDto project1 = db.components().insertPrivateProject();
CustomMeasureDto measure1 = db.measures().insertCustomMeasure(user, project1, metric);
CustomMeasureDto measure2 = db.measures().insertCustomMeasure(user, project1, metric);
ComponentDto project2 = db.components().insertPrivateProject();
CustomMeasureDto measure3 = db.measures().insertCustomMeasure(user, project2, metric);

assertThat(underTest.selectByComponentUuid(session, project1.uuid(), 0, 100))
.extracting(CustomMeasureDto::getId, CustomMeasureDto::getComponentUuid)
.containsOnly(
tuple(measure1.getId(), project1.uuid()),
tuple(measure2.getId(), project1.uuid()))
.doesNotContain(tuple(measure3.getId(), project2.uuid()));
}

@Test
@@ -146,22 +161,21 @@ public class CustomMeasureDaoTest {
assertThat(count).isEqualTo(2);
}

@Test
public void select_by_id_fail_if_no_measure_found() {
expectedException.expect(RowNotFoundException.class);

underTest.selectOrFail(session, 42L);
}

@Test
public void select_by_metric_key_and_text_value() {
db.prepareDbUnit(getClass(), "select_by_metric_key_and_text_value.xml");

List<CustomMeasureDto> result = underTest.selectByMetricKeyAndTextValue(session, "customKey", "value1");

assertThat(result).extracting("id").containsOnly(20L, 21L);

assertThat(underTest.selectByMetricKeyAndTextValue(session, "customKey", "unknown")).isEmpty();
assertThat(underTest.selectByMetricKeyAndTextValue(session, "unknown", "value1")).isEmpty();
UserDto user = db.users().insertUser();
ComponentDto project = db.components().insertPrivateProject();
MetricDto metric = db.measures().insertMetric(m -> m.setUserManaged(true));
CustomMeasureDto customMeasure1 = db.measures().insertCustomMeasure(user, project, metric, m -> m.setTextValue("value"));
CustomMeasureDto customMeasure2 = db.measures().insertCustomMeasure(user, project, metric, m -> m.setTextValue("value"));
CustomMeasureDto customMeasure3 = db.measures().insertCustomMeasure(user, project, metric, m -> m.setTextValue("other value"));

assertThat(underTest.selectByMetricKeyAndTextValue(session, metric.getKey(), "value"))
.extracting(CustomMeasureDto::getId)
.containsExactlyInAnyOrder(customMeasure1.getId(), customMeasure2.getId())
.doesNotContain(customMeasure3.getId());

assertThat(underTest.selectByMetricKeyAndTextValue(session, metric.getKey(), "unknown")).isEmpty();
assertThat(underTest.selectByMetricKeyAndTextValue(session, "unknown", "value")).isEmpty();
}
}

+ 1
- 1
server/sonar-db-dao/src/test/java/org/sonar/db/measure/custom/CustomMeasureTesting.java View File

@@ -32,7 +32,7 @@ public class CustomMeasureTesting {
return new CustomMeasureDto()
.setDescription(RandomStringUtils.randomAlphanumeric(255))
.setTextValue(RandomStringUtils.randomAlphanumeric(255))
.setUserLogin(RandomStringUtils.randomAlphanumeric(255))
.setUserUuid("userUuid" + RandomStringUtils.randomAlphanumeric(100))
.setValue(RandomUtils.nextDouble())
.setMetricId(RandomUtils.nextInt())
.setComponentUuid(RandomStringUtils.randomAlphanumeric(50))

+ 0
- 16
server/sonar-db-dao/src/test/java/org/sonar/db/metric/MetricDaoTest.java View File

@@ -197,22 +197,6 @@ public class MetricDaoTest {
assertThat(result).isNotNull();
}

@Test
public void selectOrFailById() {
MetricDto metric = underTest.insert(dbSession, newMetricDto());

MetricDto result = underTest.selectOrFailById(dbSession, metric.getId());

assertThat(result).isNotNull();
}

@Test
public void fail_when_no_id_selectOrFailById() {
expectedException.expect(RowNotFoundException.class);

underTest.selectOrFailById(dbSession, 42L);
}

@Test
public void selectByIds() {
MetricDto metric1 = underTest.insert(dbSession, newMetricDto());

+ 3
- 18
server/sonar-db-dao/src/test/java/org/sonar/db/user/UserDaoTest.java View File

@@ -26,7 +26,6 @@ import java.util.List;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
import org.sonar.api.user.UserQuery;
import org.sonar.api.utils.DateUtils;
import org.sonar.api.utils.System2;
@@ -34,7 +33,6 @@ import org.sonar.db.DatabaseUtils;
import org.sonar.db.DbClient;
import org.sonar.db.DbSession;
import org.sonar.db.DbTester;
import org.sonar.db.RowNotFoundException;
import org.sonar.db.component.ComponentDto;
import org.sonar.db.organization.OrganizationDto;

@@ -43,7 +41,6 @@ import static java.util.Collections.emptyList;
import static org.apache.commons.lang.RandomStringUtils.randomAlphanumeric;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.groups.Tuple.tuple;
import static org.junit.Assert.fail;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import static org.sonar.db.user.GroupTesting.newGroupDto;
@@ -54,8 +51,6 @@ public class UserDaoTest {

private System2 system2 = mock(System2.class);

@Rule
public ExpectedException thrown = ExpectedException.none();
@Rule
public DbTester db = DbTester.create(system2);

@@ -509,7 +504,7 @@ public class UserDaoTest {
underTest.insert(session, user);
session.commit();

underTest.cleanHomepage(session,user);
underTest.cleanHomepage(session, user);

UserDto reloaded = underTest.selectUserById(session, user.getId());
assertThat(reloaded.getUpdatedAt()).isEqualTo(NOW);
@@ -538,7 +533,7 @@ public class UserDaoTest {
UserDto user2 = db.users().insertUser();
underTest.setRoot(session, user2.getLogin(), true);

UserDto dto = underTest.selectOrFailByLogin(session, user1.getLogin());
UserDto dto = underTest.selectByLogin(session, user1.getLogin());
assertThat(dto.getId()).isEqualTo(user1.getId());
assertThat(dto.getLogin()).isEqualTo("marius");
assertThat(dto.getName()).isEqualTo("Marius");
@@ -553,7 +548,7 @@ public class UserDaoTest {
assertThat(dto.getHomepageType()).isEqualTo("project");
assertThat(dto.getHomepageParameter()).isEqualTo("OB1");

dto = underTest.selectOrFailByLogin(session, user2.getLogin());
dto = underTest.selectByLogin(session, user2.getLogin());
assertThat(dto.isRoot()).isTrue();
}

@@ -579,16 +574,6 @@ public class UserDaoTest {
assertThat(results).hasSize(2);
}

@Test
public void select_by_login_with_unknown_login() {
try {
underTest.selectOrFailByLogin(session, "unknown");
fail();
} catch (Exception e) {
assertThat(e).isInstanceOf(RowNotFoundException.class).hasMessage("User with login 'unknown' has not been found");
}
}

@Test
public void select_nullable_by_login() {
db.users().insertUser(user -> user.setLogin("marius"));

+ 0
- 14
server/sonar-db-dao/src/test/resources/org/sonar/db/measure/custom/CustomMeasureDaoTest/select_by_metric_key_and_text_value.xml View File

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

<metrics id="10" name="ncloc"/>
<metrics id="11" name="customKey"/>

<manual_measures id="20" metric_id="11" component_uuid="ABCD" value="[null]" text_value="value1"
user_login="seb" description="" created_at="123456789" updated_at="123456789" />
<manual_measures id="21" metric_id="11" component_uuid="BCDE" value="[null]" text_value="value1"
user_login="seb" description="" created_at="123456789" updated_at="123456789" />
<manual_measures id="22" metric_id="11" component_uuid="CDEF" value="[null]" text_value="value2"
user_login="seb" description="" created_at="123456789" updated_at="123456789" />


</dataset>

+ 1
- 0
server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v72/DbVersion72.java View File

@@ -47,6 +47,7 @@ public class DbVersion72 implements DbVersion {
.add(2117, "Drop USER_ID from table organizations", DropUserIdFromOrganizations.class)
.add(2118, "Rename USER_LOGIN TO USER_UUID on table QPROFILE_CHANGES", RenameUserLoginToUserUuidOnTableQProfileChanges.class)
.add(2119, "Rename LOGIN TO USER_UUID on table USER_TOKENS", RenameLoginToUserUuidOnTableUserTokens.class)
.add(2120, "Rename USER_LOGIN TO USER_UUID on table MANUAL_MEASURES", RenameUserLoginToUserUuidOnTableManualMeasures.class);
;
}
}

+ 48
- 0
server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v72/RenameUserLoginToUserUuidOnTableManualMeasures.java View File

@@ -0,0 +1,48 @@
/*
* SonarQube
* Copyright (C) 2009-2018 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.server.platform.db.migration.version.v72;

import java.sql.SQLException;
import org.sonar.db.Database;
import org.sonar.server.platform.db.migration.sql.RenameColumnsBuilder;
import org.sonar.server.platform.db.migration.step.DdlChange;

import static org.sonar.server.platform.db.migration.def.VarcharColumnDef.newVarcharColumnDefBuilder;

public class RenameUserLoginToUserUuidOnTableManualMeasures extends DdlChange {

private static final String TABLE = "manual_measures";

public RenameUserLoginToUserUuidOnTableManualMeasures(Database db) {
super(db);
}

@Override
public void execute(Context context) throws SQLException {
context.execute(new RenameColumnsBuilder(getDialect(), TABLE)
.renameColumn("user_login",
newVarcharColumnDefBuilder()
.setColumnName("user_uuid")
.setLimit(255)
.setIsNullable(true)
.build())
.build());
}
}

+ 1
- 1
server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v72/DbVersion72Test.java View File

@@ -34,7 +34,7 @@ public class DbVersion72Test {

@Test
public void verify_migration_count() {
verifyMigrationCount(underTest, 20);
verifyMigrationCount(underTest, 21);
}

}

+ 57
- 0
server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v72/RenameUserLoginToUserUuidOnTableManualMeasuresTest.java View File

@@ -0,0 +1,57 @@
/*
* SonarQube
* Copyright (C) 2009-2018 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.server.platform.db.migration.version.v72;

import java.sql.SQLException;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
import org.sonar.db.CoreDbTester;

import static java.sql.Types.VARCHAR;

public class RenameUserLoginToUserUuidOnTableManualMeasuresTest {

@Rule
public final CoreDbTester db = CoreDbTester.createForSchema(RenameUserLoginToUserUuidOnTableManualMeasuresTest.class, "manual_measures.sql");

@Rule
public ExpectedException expectedException = ExpectedException.none();

private RenameUserLoginToUserUuidOnTableManualMeasures underTest = new RenameUserLoginToUserUuidOnTableManualMeasures(db.database());

@Test
public void rename_column() throws SQLException {
underTest.execute();

db.assertColumnDefinition("manual_measures", "user_uuid", VARCHAR, 255, true);
db.assertColumnDoesNotExist("manual_measures", "user_login");
}

public void migration_is_not_reentrant() throws SQLException {
underTest.execute();

expectedException.expect(IllegalStateException.class);

underTest.execute();
}

}

+ 12
- 0
server/sonar-db-migration/src/test/resources/org/sonar/server/platform/db/migration/version/v72/RenameUserLoginToUserUuidOnTableManualMeasuresTest/manual_measures.sql View File

@@ -0,0 +1,12 @@
CREATE TABLE "MANUAL_MEASURES" (
"ID" BIGINT NOT NULL GENERATED BY DEFAULT AS IDENTITY (START WITH 1, INCREMENT BY 1),
"METRIC_ID" INTEGER NOT NULL,
"COMPONENT_UUID" VARCHAR(50) NOT NULL,
"VALUE" DOUBLE,
"TEXT_VALUE" VARCHAR(4000),
"USER_LOGIN" VARCHAR(255),
"DESCRIPTION" VARCHAR(4000),
"CREATED_AT" BIGINT,
"UPDATED_AT" BIGINT
);
CREATE INDEX "MANUAL_MEASURES_COMPONENT_UUID" ON "MANUAL_MEASURES" ("COMPONENT_UUID");

+ 16
- 9
server/sonar-server/src/main/java/org/sonar/server/measure/custom/ws/CreateAction.java View File

@@ -35,6 +35,8 @@ import org.sonar.server.component.ComponentFinder;
import org.sonar.server.user.UserSession;

import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkState;
import static java.util.Objects.requireNonNull;
import static org.sonar.server.component.ComponentFinder.ParamNames.PROJECT_ID_AND_KEY;
import static org.sonar.server.measure.custom.ws.CustomMeasureValidator.checkPermissions;
import static org.sonar.server.measure.custom.ws.CustomMeasureValueDescription.measureValueDescription;
@@ -115,12 +117,14 @@ public class CreateAction implements CustomMeasuresWsAction {
checkPermissions(userSession, component);
checkIsProjectOrModule(component);
checkMeasureDoesNotExistAlready(dbSession, component, metric);
UserDto user = dbClient.userDao().selectOrFailByLogin(dbSession, userSession.getLogin());
String userUuid = requireNonNull(userSession.getUuid(), "User uuid should not be null");
UserDto user = dbClient.userDao().selectByUuid(dbSession, userUuid);
checkState(user != null, "User with uuid '%s' does not exist", userUuid);
CustomMeasureDto measure = new CustomMeasureDto()
.setComponentUuid(component.uuid())
.setMetricId(metric.getId())
.setDescription(description)
.setUserLogin(user.getLogin())
.setUserUuid(user.getUuid())
.setCreatedAt(now)
.setUpdatedAt(now);
validator.setMeasureValue(measure, valueAsString, metric);
@@ -134,14 +138,14 @@ public class CreateAction implements CustomMeasuresWsAction {
}

private static void checkIsProjectOrModule(ComponentDto component) {
checkRequest(Scopes.PROJECT.equals(component.scope()), "Component '%s' (id: %s) must be a project or a module.", component.getDbKey(), component.uuid());
checkRequest(Scopes.PROJECT.equals(component.scope()), "Component '%s' must be a project or a module.", component.getDbKey());
}

private void checkMeasureDoesNotExistAlready(DbSession dbSession, ComponentDto component, MetricDto metric) {
int nbMeasuresOnSameMetricAndMeasure = dbClient.customMeasureDao().countByComponentIdAndMetricId(dbSession, component.uuid(), metric.getId());
checkRequest(nbMeasuresOnSameMetricAndMeasure == 0,
"A measure already exists for project '%s' (id: %s) and metric '%s' (id: '%d')",
component.getDbKey(), component.uuid(), metric.getKey(), metric.getId());
"A measure already exists for project '%s' and metric '%s'",
component.getDbKey(), metric.getKey());
}

private MetricDto searchMetric(DbSession dbSession, Request request) {
@@ -149,10 +153,13 @@ public class CreateAction implements CustomMeasuresWsAction {
String metricKey = request.param(PARAM_METRIC_KEY);
checkArgument(metricId != null ^ metricKey != null, "Either the metric id or the metric key must be provided");

if (metricId != null) {
return dbClient.metricDao().selectOrFailById(dbSession, metricId);
if (metricId == null) {
MetricDto metric = dbClient.metricDao().selectByKey(dbSession, metricKey);
checkArgument(metric != null, "Metric with key '%s' does not exist", metricKey);
return metric;
}

return dbClient.metricDao().selectOrFailByKey(dbSession, metricKey);
MetricDto metric = dbClient.metricDao().selectById(dbSession, metricId);
checkArgument(metric != null, "Metric with id '%s' does not exist", metricId);
return metric;
}
}

+ 2
- 13
server/sonar-server/src/main/java/org/sonar/server/measure/custom/ws/CustomMeasureJsonWriter.java View File

@@ -22,8 +22,6 @@ package org.sonar.server.measure.custom.ws;
import com.google.common.collect.ImmutableSet;
import java.util.Collection;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.Set;
import javax.annotation.Nullable;
import org.sonar.api.measures.Metric;
@@ -59,7 +57,8 @@ public class CustomMeasureJsonWriter {
this.userJsonWriter = userJsonWriter;
}

public void write(JsonWriter json, CustomMeasureDto measure, MetricDto metric, ComponentDto component, UserDto user, boolean isPending, Collection<String> fieldsToReturn) {
public void write(JsonWriter json, CustomMeasureDto measure, MetricDto metric, ComponentDto component, UserDto user, boolean isPending,
@Nullable Collection<String> fieldsToReturn) {
json.beginObject();
json.prop(FIELD_ID, String.valueOf(measure.getId()));
writeIfNeeded(json, measureValue(measure, metric), FIELD_VALUE, fieldsToReturn);
@@ -109,14 +108,4 @@ public class CustomMeasureJsonWriter {
}
}

public void write(JsonWriter json, List<CustomMeasureDto> customMeasures, ComponentDto project, Map<Integer, MetricDto> metricsById, Map<String, UserDto> usersByLogin,
@Nullable Long lastAnalysisTimestamp, Collection<String> fieldsToReturn) {
json.name("customMeasures");
json.beginArray();
for (CustomMeasureDto customMeasure : customMeasures) {
boolean pending = lastAnalysisTimestamp == null || lastAnalysisTimestamp < customMeasure.getUpdatedAt();
write(json, customMeasure, metricsById.get(customMeasure.getMetricId()), project, usersByLogin.get(customMeasure.getUserLogin()), pending, fieldsToReturn);
}
json.endArray();
}
}

+ 4
- 1
server/sonar-server/src/main/java/org/sonar/server/measure/custom/ws/DeleteAction.java View File

@@ -29,6 +29,8 @@ import org.sonar.db.component.ComponentDto;
import org.sonar.db.measure.custom.CustomMeasureDto;
import org.sonar.server.user.UserSession;

import static com.google.common.base.Preconditions.checkArgument;

public class DeleteAction implements CustomMeasuresWsAction {

private static final String ACTION = "delete";
@@ -61,7 +63,8 @@ public class DeleteAction implements CustomMeasuresWsAction {
long id = request.mandatoryParamAsLong(PARAM_ID);

try (DbSession dbSession = dbClient.openSession(false)) {
CustomMeasureDto customMeasure = dbClient.customMeasureDao().selectOrFail(dbSession, id);
CustomMeasureDto customMeasure = dbClient.customMeasureDao().selectById(dbSession, id);
checkArgument(customMeasure != null, "Custom measure with id '%s' does not exist", id);
checkPermission(dbSession, customMeasure);
dbClient.customMeasureDao().delete(dbSession, id);
dbSession.commit();

+ 23
- 29
server/sonar-server/src/main/java/org/sonar/server/measure/custom/ws/SearchAction.java View File

@@ -20,12 +20,13 @@
package org.sonar.server.measure.custom.ws;

import com.google.common.base.Function;
import com.google.common.collect.FluentIterable;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import javax.annotation.CheckForNull;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
@@ -33,6 +34,7 @@ import org.sonar.api.server.ws.Request;
import org.sonar.api.server.ws.Response;
import org.sonar.api.server.ws.WebService;
import org.sonar.api.utils.text.JsonWriter;
import org.sonar.core.util.stream.MoreCollectors;
import org.sonar.db.DbClient;
import org.sonar.db.DbSession;
import org.sonar.db.component.ComponentDto;
@@ -103,10 +105,10 @@ public class SearchAction implements CustomMeasuresWsAction {
Long lastAnalysisDateMs = searchLastSnapshotDate(dbSession, component);
List<CustomMeasureDto> customMeasures = searchCustomMeasures(dbSession, component, searchOptions);
int nbCustomMeasures = countTotalOfCustomMeasures(dbSession, component);
Map<String, UserDto> usersByLogin = usersByLogin(dbSession, customMeasures);
Map<String, UserDto> usersByUuid = usersByUuid(dbSession, customMeasures);
Map<Integer, MetricDto> metricsById = metricsById(dbSession, customMeasures);

writeResponse(response, customMeasures, nbCustomMeasures, component, metricsById, usersByLogin, lastAnalysisDateMs, searchOptions, fieldsToReturn);
writeResponse(response, customMeasures, nbCustomMeasures, component, metricsById, usersByUuid, lastAnalysisDateMs, searchOptions, fieldsToReturn);
}
}

@@ -126,44 +128,36 @@ public class SearchAction implements CustomMeasuresWsAction {
}

private void writeResponse(Response response, List<CustomMeasureDto> customMeasures, int nbCustomMeasures, ComponentDto project, Map<Integer, MetricDto> metricsById,
Map<String, UserDto> usersByLogin, @Nullable Long lastAnalysisDate, SearchOptions searchOptions, List<String> fieldsToReturn) {
Map<String, UserDto> usersByUuid, @Nullable Long lastAnalysisDate, SearchOptions searchOptions, @Nullable List<String> fieldsToReturn) {
JsonWriter json = response.newJsonWriter();
json.beginObject();
customMeasureJsonWriter.write(json, customMeasures, project, metricsById, usersByLogin, lastAnalysisDate, fieldsToReturn);
writeUsers(json, customMeasures, project, metricsById, usersByUuid, lastAnalysisDate, fieldsToReturn);
searchOptions.writeJson(json, nbCustomMeasures);
json.endObject();
json.close();
}

private void writeUsers(JsonWriter json, List<CustomMeasureDto> customMeasures, ComponentDto project, Map<Integer, MetricDto> metricsById, Map<String, UserDto> usersByUuids,
@Nullable Long lastAnalysisTimestamp, @Nullable Collection<String> fieldsToReturn) {
json.name("customMeasures");
json.beginArray();
for (CustomMeasureDto customMeasure : customMeasures) {
boolean pending = lastAnalysisTimestamp == null || lastAnalysisTimestamp < customMeasure.getUpdatedAt();
customMeasureJsonWriter.write(json, customMeasure, metricsById.get(customMeasure.getMetricId()), project, usersByUuids.get(customMeasure.getUserUuid()), pending,
fieldsToReturn);
}
json.endArray();
}

private Map<Integer, MetricDto> metricsById(DbSession dbSession, List<CustomMeasureDto> customMeasures) {
List<MetricDto> metrics = dbClient.metricDao().selectByIds(dbSession, newHashSet(Lists.transform(customMeasures, CustomMeasureToMetricIdFunction.INSTANCE)));
return Maps.uniqueIndex(metrics, MetricToIdFunction.INSTANCE);
}

private Map<String, UserDto> usersByLogin(DbSession dbSession, List<CustomMeasureDto> customMeasures) {
List<String> logins = FluentIterable.from(customMeasures)
.transform(CustomMeasureToUserLoginFunction.INSTANCE)
.toList();
List<UserDto> userDtos = dbClient.userDao().selectByLogins(dbSession, logins);
return FluentIterable.from(userDtos).uniqueIndex(UserDtoToLogin.INSTANCE);
}

private enum CustomMeasureToUserLoginFunction implements Function<CustomMeasureDto, String> {
INSTANCE;

@Override
public String apply(@Nonnull CustomMeasureDto customMeasure) {
return customMeasure.getUserLogin();
}
}

private enum UserDtoToLogin implements Function<UserDto, String> {
INSTANCE;

@Override
public String apply(@Nonnull UserDto input) {
return input.getLogin();
}
private Map<String, UserDto> usersByUuid(DbSession dbSession, List<CustomMeasureDto> customMeasures) {
Set<String> userUuids = customMeasures.stream().map(CustomMeasureDto::getUserUuid).collect(MoreCollectors.toSet());
List<UserDto> users = dbClient.userDao().selectByUuids(dbSession, userUuids);
return users.stream().collect(MoreCollectors.uniqueIndex(UserDto::getUuid));
}

private enum CustomMeasureToMetricIdFunction implements Function<CustomMeasureDto, Integer> {

+ 12
- 4
server/sonar-server/src/main/java/org/sonar/server/measure/custom/ws/UpdateAction.java View File

@@ -33,6 +33,9 @@ import org.sonar.db.metric.MetricDto;
import org.sonar.db.user.UserDto;
import org.sonar.server.user.UserSession;

import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkState;
import static java.util.Objects.requireNonNull;
import static org.sonar.server.measure.custom.ws.CustomMeasureValidator.checkPermissions;
import static org.sonar.server.measure.custom.ws.CustomMeasureValueDescription.measureValueDescription;

@@ -86,15 +89,20 @@ public class UpdateAction implements CustomMeasuresWsAction {
checkParameters(value, description);

try (DbSession dbSession = dbClient.openSession(true)) {
CustomMeasureDto customMeasure = dbClient.customMeasureDao().selectOrFail(dbSession, id);
MetricDto metric = dbClient.metricDao().selectOrFailById(dbSession, customMeasure.getMetricId());
CustomMeasureDto customMeasure = dbClient.customMeasureDao().selectById(dbSession, id);
checkArgument(customMeasure != null, "Custom measure with id '%s' does not exist", id);
int customMetricId = customMeasure.getMetricId();
MetricDto metric = dbClient.metricDao().selectById(dbSession, customMetricId);
checkState(metric != null, "Metric with id '%s' does not exist", customMetricId);
ComponentDto component = dbClient.componentDao().selectOrFailByUuid(dbSession, customMeasure.getComponentUuid());
checkPermissions(userSession, component);
UserDto user = dbClient.userDao().selectOrFailByLogin(dbSession, userSession.getLogin());
String userUuid = requireNonNull(userSession.getUuid(), "User uuid should not be null");
UserDto user = dbClient.userDao().selectByUuid(dbSession, userUuid);
checkState(user != null, "User with uuid '%s' does not exist", userUuid);

setValue(customMeasure, value, metric);
setDescription(customMeasure, description);
customMeasure.setUserLogin(user.getLogin());
customMeasure.setUserUuid(user.getUuid());
customMeasure.setUpdatedAt(system.now());
dbClient.customMeasureDao().update(dbSession, customMeasure);
dbSession.commit();

+ 274
- 234
server/sonar-server/src/test/java/org/sonar/server/measure/custom/ws/CreateActionTest.java View File

@@ -19,52 +19,43 @@
*/
package org.sonar.server.measure.custom.ws;

import java.util.List;
import org.assertj.core.data.Offset;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
import org.sonar.api.measures.Metric;
import org.sonar.api.measures.Metric.ValueType;
import org.sonar.api.utils.System2;
import org.sonar.api.web.UserRole;
import org.sonar.db.DbClient;
import org.sonar.db.DbSession;
import org.sonar.db.DbTester;
import org.sonar.db.RowNotFoundException;
import org.sonar.db.component.ComponentDto;
import org.sonar.db.component.ComponentTesting;
import org.sonar.db.measure.custom.CustomMeasureDto;
import org.sonar.db.metric.MetricDto;
import org.sonar.db.metric.MetricTesting;
import org.sonar.db.organization.OrganizationDto;
import org.sonar.db.permission.OrganizationPermission;
import org.sonar.db.user.UserDto;
import org.sonar.server.component.TestComponentFinder;
import org.sonar.server.es.EsTester;
import org.sonar.server.exceptions.BadRequestException;
import org.sonar.server.exceptions.ForbiddenException;
import org.sonar.server.exceptions.NotFoundException;
import org.sonar.server.exceptions.ServerException;
import org.sonar.server.tester.UserSessionRule;
import org.sonar.server.user.ws.UserJsonWriter;
import org.sonar.server.ws.WsTester;
import org.sonar.server.ws.WsActionTester;

import static java.lang.String.format;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.data.Offset.offset;
import static org.assertj.core.api.Assertions.tuple;
import static org.sonar.api.measures.Metric.ValueType.BOOL;
import static org.sonar.api.measures.Metric.ValueType.FLOAT;
import static org.sonar.api.measures.Metric.ValueType.INT;
import static org.sonar.api.measures.Metric.ValueType.LEVEL;
import static org.sonar.api.measures.Metric.ValueType.STRING;
import static org.sonar.api.measures.Metric.ValueType.WORK_DUR;
import static org.sonar.api.web.UserRole.ADMIN;
import static org.sonar.db.component.ComponentTesting.newModuleDto;
import static org.sonar.server.util.TypeValidationsTesting.newFullTypeValidations;
import static org.sonar.test.JsonAssert.assertJson;

public class CreateActionTest {

private static final String DEFAULT_PROJECT_UUID = "project-uuid";
private static final String DEFAULT_PROJECT_KEY = "project-key";

@Rule
public UserSessionRule userSession = UserSessionRule.standalone();
@Rule
@@ -74,284 +65,311 @@ public class CreateActionTest {
@Rule
public EsTester es = EsTester.create();

private DbClient dbClient = db.getDbClient();
private ComponentDto project;
private final DbSession dbSession = db.getSession();
private WsTester ws;

@Before
public void setUp() {
ws = new WsTester(new CustomMeasuresWs(new CreateAction(dbClient, userSession, System2.INSTANCE, new CustomMeasureValidator(newFullTypeValidations()),
new CustomMeasureJsonWriter(new UserJsonWriter(userSession)), TestComponentFinder.from(db))));

db.users().insertUser(u -> u.setLogin("login")
.setName("Login")
.setEmail("login@login.com")
.setActive(true));

OrganizationDto organizationDto = db.organizations().insert();
project = ComponentTesting.newPrivateProjectDto(organizationDto, DEFAULT_PROJECT_UUID).setDbKey(DEFAULT_PROJECT_KEY);
dbClient.componentDao().insert(dbSession, project);
dbSession.commit();
userSession.logIn("login").addProjectPermission(UserRole.ADMIN, project);
}
private WsActionTester ws = new WsActionTester(
new CreateAction(db.getDbClient(), userSession, System2.INSTANCE, new CustomMeasureValidator(newFullTypeValidations()),
new CustomMeasureJsonWriter(new UserJsonWriter(userSession)), TestComponentFinder.from(db)));

@Test
public void create_boolean_custom_measure_in_db() throws Exception {
MetricDto metric = insertMetric(BOOL);

newRequest()
.setParam(CreateAction.PARAM_PROJECT_ID, DEFAULT_PROJECT_UUID)
.setParam(CreateAction.PARAM_METRIC_ID, metric.getId().toString())
public void create_boolean_custom_measure_in_db() {
MetricDto metric = db.measures().insertMetric(m -> m.setUserManaged(true).setValueType(BOOL.name()));
ComponentDto project = db.components().insertPrivateProject();
UserDto user = db.users().insertUser();
userSession.logIn(user).addProjectPermission(ADMIN, project);

ws.newRequest()
.setParam(CreateAction.PARAM_PROJECT_ID, project.uuid())
.setParam(CreateAction.PARAM_METRIC_ID, Integer.toString(metric.getId()))
.setParam(CreateAction.PARAM_DESCRIPTION, "custom-measure-description")
.setParam(CreateAction.PARAM_VALUE, "true")
.execute();

List<CustomMeasureDto> customMeasures = dbClient.customMeasureDao().selectByMetricId(dbSession, metric.getId());
CustomMeasureDto customMeasure = customMeasures.get(0);
assertThat(customMeasures).hasSize(1);
assertThat(customMeasure.getDescription()).isEqualTo("custom-measure-description");
assertThat(customMeasure.getTextValue()).isNullOrEmpty();
assertThat(customMeasure.getValue()).isCloseTo(1.0d, offset(0.01d));
assertThat(customMeasure.getComponentUuid()).isEqualTo(DEFAULT_PROJECT_UUID);
assertThat(db.getDbClient().customMeasureDao().selectByMetricId(db.getSession(), metric.getId()))
.extracting(CustomMeasureDto::getDescription, CustomMeasureDto::getTextValue, CustomMeasureDto::getValue, CustomMeasureDto::getComponentUuid)
.containsExactlyInAnyOrder(tuple("custom-measure-description", null, 1d, project.uuid()));
}

@Test
public void create_int_custom_measure_in_db() throws Exception {
MetricDto metric = insertMetric(INT);

newRequest()
.setParam(CreateAction.PARAM_PROJECT_ID, DEFAULT_PROJECT_UUID)
public void create_int_custom_measure_in_db() {
MetricDto metric = db.measures().insertMetric(m -> m.setUserManaged(true).setValueType(INT.name()));
ComponentDto project = db.components().insertPrivateProject();
UserDto user = db.users().insertUser();
userSession.logIn(user).addProjectPermission(ADMIN, project);

ws.newRequest()
.setParam(CreateAction.PARAM_PROJECT_ID, project.uuid())
.setParam(CreateAction.PARAM_METRIC_ID, metric.getId().toString())
.setParam(CreateAction.PARAM_VALUE, "42")
.execute();

CustomMeasureDto customMeasure = dbClient.customMeasureDao().selectByMetricId(dbSession, metric.getId()).get(0);
assertThat(customMeasure.getTextValue()).isNullOrEmpty();
assertThat(customMeasure.getValue()).isCloseTo(42.0d, offset(0.01d));
}

@Test
public void create_text_custom_measure_in_db() throws Exception {
MetricDto metric = insertMetric(STRING);

newRequest()
.setParam(CreateAction.PARAM_PROJECT_ID, DEFAULT_PROJECT_UUID)
.setParam(CreateAction.PARAM_METRIC_ID, metric.getId().toString())
.setParam(CreateAction.PARAM_VALUE, "custom-measure-free-text")
.execute();

CustomMeasureDto customMeasure = dbClient.customMeasureDao().selectByMetricId(dbSession, metric.getId()).get(0);
assertThat(customMeasure.getTextValue()).isEqualTo("custom-measure-free-text");
assertThat(db.getDbClient().customMeasureDao().selectByMetricId(db.getSession(), metric.getId()))
.extracting(CustomMeasureDto::getDescription, CustomMeasureDto::getTextValue, CustomMeasureDto::getValue, CustomMeasureDto::getComponentUuid)
.containsExactlyInAnyOrder(tuple(null, null, 42d, project.uuid()));
}

@Test
public void create_text_custom_measure_as_project_admin() throws Exception {
MetricDto metric = insertMetric(STRING);
userSession.logIn("login").addProjectPermission(UserRole.ADMIN, project);

newRequest()
.setParam(CreateAction.PARAM_PROJECT_ID, DEFAULT_PROJECT_UUID)
public void create_text_custom_measure_in_db() {
MetricDto metric = db.measures().insertMetric(m -> m.setUserManaged(true).setValueType(STRING.name()));
ComponentDto project = db.components().insertPrivateProject();
UserDto user = db.users().insertUser();
userSession.logIn(user).addProjectPermission(ADMIN, project);

ws.newRequest()
.setParam(CreateAction.PARAM_PROJECT_ID, project.uuid())
.setParam(CreateAction.PARAM_METRIC_ID, metric.getId().toString())
.setParam(CreateAction.PARAM_VALUE, "custom-measure-free-text")
.execute();

CustomMeasureDto customMeasure = dbClient.customMeasureDao().selectByMetricId(dbSession, metric.getId()).get(0);
assertThat(customMeasure).isNotNull();
assertThat(db.getDbClient().customMeasureDao().selectByMetricId(db.getSession(), metric.getId()))
.extracting(CustomMeasureDto::getDescription, CustomMeasureDto::getTextValue, CustomMeasureDto::getValue, CustomMeasureDto::getComponentUuid)
.containsExactlyInAnyOrder(tuple(null, "custom-measure-free-text", 0d, project.uuid()));
}

@Test
public void create_text_custom_measure_with_metric_key() throws Exception {
MetricDto metric = insertMetric(STRING);

newRequest()
.setParam(CreateAction.PARAM_PROJECT_ID, DEFAULT_PROJECT_UUID)
public void create_text_custom_measure_with_metric_key() {
MetricDto metric = db.measures().insertMetric(m -> m.setUserManaged(true).setValueType(STRING.name()));
ComponentDto project = db.components().insertPrivateProject();
UserDto user = db.users().insertUser();
userSession.logIn(user).addProjectPermission(ADMIN, project);

ws.newRequest()
.setParam(CreateAction.PARAM_PROJECT_ID, project.uuid())
.setParam(CreateAction.PARAM_METRIC_KEY, metric.getKey())
.setParam(CreateAction.PARAM_VALUE, "whatever-value")
.execute();

CustomMeasureDto customMeasure = dbClient.customMeasureDao().selectByMetricId(dbSession, metric.getId()).get(0);
assertThat(customMeasure).isNotNull();
assertThat(db.getDbClient().customMeasureDao().selectByMetricId(db.getSession(), metric.getId()))
.extracting(CustomMeasureDto::getDescription, CustomMeasureDto::getTextValue, CustomMeasureDto::getValue, CustomMeasureDto::getComponentUuid)
.containsExactlyInAnyOrder(tuple(null, "whatever-value", 0d, project.uuid()));
}

@Test
public void create_text_custom_measure_with_project_key() throws Exception {
MetricDto metric = insertMetric(STRING);

newRequest()
.setParam(CreateAction.PARAM_PROJECT_KEY, DEFAULT_PROJECT_KEY)
public void create_text_custom_measure_with_project_key() {
MetricDto metric = db.measures().insertMetric(m -> m.setUserManaged(true).setValueType(STRING.name()));
ComponentDto project = db.components().insertPrivateProject();
UserDto user = db.users().insertUser();
userSession.logIn(user).addProjectPermission(ADMIN, project);

ws.newRequest()
.setParam(CreateAction.PARAM_PROJECT_KEY, project.getKey())
.setParam(CreateAction.PARAM_METRIC_ID, metric.getId().toString())
.setParam(CreateAction.PARAM_VALUE, "whatever-value")
.execute();

CustomMeasureDto customMeasure = dbClient.customMeasureDao().selectByMetricId(dbSession, metric.getId()).get(0);
assertThat(customMeasure).isNotNull();
assertThat(db.getDbClient().customMeasureDao().selectByMetricId(db.getSession(), metric.getId()))
.extracting(CustomMeasureDto::getDescription, CustomMeasureDto::getTextValue, CustomMeasureDto::getValue, CustomMeasureDto::getComponentUuid)
.containsExactlyInAnyOrder(tuple(null, "whatever-value", 0d, project.uuid()));
}

@Test
public void create_float_custom_measure_in_db() throws Exception {
MetricDto metric = insertMetric(FLOAT);

newRequest()
.setParam(CreateAction.PARAM_PROJECT_ID, DEFAULT_PROJECT_UUID)
public void create_float_custom_measure_in_db() {
MetricDto metric = db.measures().insertMetric(m -> m.setUserManaged(true).setValueType(FLOAT.name()));
ComponentDto project = db.components().insertPrivateProject();
UserDto user = db.users().insertUser();
userSession.logIn(user).addProjectPermission(ADMIN, project);

ws.newRequest()
.setParam(CreateAction.PARAM_PROJECT_ID, project.uuid())
.setParam(CreateAction.PARAM_METRIC_ID, metric.getId().toString())
.setParam(CreateAction.PARAM_VALUE, "4.2")
.execute();

CustomMeasureDto customMeasure = dbClient.customMeasureDao().selectByMetricId(dbSession, metric.getId()).get(0);
assertThat(customMeasure.getValue()).isCloseTo(4.2d, Offset.offset(0.01d));
assertThat(customMeasure.getTextValue()).isNullOrEmpty();
assertThat(db.getDbClient().customMeasureDao().selectByMetricId(db.getSession(), metric.getId()))
.extracting(CustomMeasureDto::getDescription, CustomMeasureDto::getTextValue, CustomMeasureDto::getValue, CustomMeasureDto::getComponentUuid)
.containsExactlyInAnyOrder(tuple(null, null, 4.2d, project.uuid()));
}

@Test
public void create_work_duration_custom_measure_in_db() throws Exception {
MetricDto metric = insertMetric(WORK_DUR);

newRequest()
.setParam(CreateAction.PARAM_PROJECT_ID, DEFAULT_PROJECT_UUID)
public void create_work_duration_custom_measure_in_db() {
MetricDto metric = db.measures().insertMetric(m -> m.setUserManaged(true).setValueType(WORK_DUR.name()));
ComponentDto project = db.components().insertPrivateProject();
UserDto user = db.users().insertUser();
userSession.logIn(user).addProjectPermission(ADMIN, project);

ws.newRequest()
.setParam(CreateAction.PARAM_PROJECT_ID, project.uuid())
.setParam(CreateAction.PARAM_METRIC_ID, metric.getId().toString())
.setParam(CreateAction.PARAM_VALUE, "253")
.execute();

CustomMeasureDto customMeasure = dbClient.customMeasureDao().selectByMetricId(dbSession, metric.getId()).get(0);
assertThat(customMeasure.getTextValue()).isNullOrEmpty();
assertThat(customMeasure.getValue()).isCloseTo(253, offset(0.01d));
assertThat(db.getDbClient().customMeasureDao().selectByMetricId(db.getSession(), metric.getId()))
.extracting(CustomMeasureDto::getDescription, CustomMeasureDto::getTextValue, CustomMeasureDto::getValue, CustomMeasureDto::getComponentUuid)
.containsExactlyInAnyOrder(tuple(null, null, 253d, project.uuid()));
}

@Test
public void create_level_type_custom_measure_in_db() throws Exception {
MetricDto metric = insertMetric(LEVEL);

newRequest()
.setParam(CreateAction.PARAM_PROJECT_ID, DEFAULT_PROJECT_UUID)
public void create_level_type_custom_measure_in_db() {
MetricDto metric = db.measures().insertMetric(m -> m.setUserManaged(true).setValueType(LEVEL.name()));
ComponentDto project = db.components().insertPrivateProject();
UserDto user = db.users().insertUser();
userSession.logIn(user).addProjectPermission(ADMIN, project);

ws.newRequest()
.setParam(CreateAction.PARAM_PROJECT_ID, project.uuid())
.setParam(CreateAction.PARAM_METRIC_ID, metric.getId().toString())
.setParam(CreateAction.PARAM_VALUE, Metric.Level.WARN.name())
.execute();

CustomMeasureDto customMeasure = dbClient.customMeasureDao().selectByMetricId(dbSession, metric.getId()).get(0);
assertThat(customMeasure.getTextValue()).isEqualTo(Metric.Level.WARN.name());
assertThat(db.getDbClient().customMeasureDao().selectByMetricId(db.getSession(), metric.getId()))
.extracting(CustomMeasureDto::getDescription, CustomMeasureDto::getTextValue, CustomMeasureDto::getValue, CustomMeasureDto::getComponentUuid)
.containsExactlyInAnyOrder(tuple(null, Metric.Level.WARN.name(), 0d, project.uuid()));
}

@Test
public void response_with_object_and_id() throws Exception {
MetricDto metric = insertMetric(STRING);

WsTester.Result response = newRequest()
.setParam(CreateAction.PARAM_PROJECT_ID, DEFAULT_PROJECT_UUID)
public void response_with_object_and_id() {
MetricDto metric = db.measures().insertMetric(m -> m.setUserManaged(true).setValueType(STRING.name()));
ComponentDto project = db.components().insertPrivateProject();
UserDto user = db.users().insertUser();
userSession.logIn(user).addProjectPermission(ADMIN, project);

String response = ws.newRequest()
.setParam(CreateAction.PARAM_PROJECT_ID, project.uuid())
.setParam(CreateAction.PARAM_METRIC_ID, metric.getId().toString())
.setParam(CreateAction.PARAM_DESCRIPTION, "custom-measure-description")
.setParam(CreateAction.PARAM_VALUE, "custom-measure-free-text")
.execute();

CustomMeasureDto customMeasure = dbClient.customMeasureDao().selectByMetricId(dbSession, metric.getId()).get(0);
response.assertJson(getClass(), "custom-measure.json");
assertThat(response.outputAsString()).matches(String.format(".*\"id\"\\s*:\\s*\"%d\".*", customMeasure.getId()));
assertThat(response.outputAsString()).matches(String.format(".*\"id\"\\s*:\\s*\"%d\".*", metric.getId()));
.execute()
.getInput();

CustomMeasureDto customMeasure = db.getDbClient().customMeasureDao().selectByMetricId(db.getSession(), metric.getId()).get(0);
assertJson(response).isSimilarTo("{\n" +
" \"id\": \"" + customMeasure.getId() + "\",\n" +
" \"value\": \"custom-measure-free-text\",\n" +
" \"description\": \"custom-measure-description\",\n" +
" \"metric\": {\n" +
" \"id\": \"" + metric.getId() + "\",\n" +
" \"key\": \"" + metric.getKey() + "\",\n" +
" \"type\": \"" + metric.getValueType() + "\",\n" +
" \"name\": \"" + metric.getShortName() + "\",\n" +
" \"domain\": \"" + metric.getDomain() + "\"\n" +
" },\n" +
" \"projectId\": \"" + project.uuid() + "\",\n" +
" \"projectKey\": \"" + project.getKey() + "\",\n" +
" \"pending\": true\n" +
"}");
}

@Test
public void create_custom_measure_on_a_view() throws Exception {
String viewUuid = "VIEW_UUID";
ComponentDto view = ComponentTesting.newView(db.organizations().insert(), viewUuid);
dbClient.componentDao().insert(dbSession, view);
dbSession.commit();
MetricDto metric = insertMetric(BOOL);
userSession.logIn("login").addProjectPermission(UserRole.ADMIN, view);

newRequest()
.setParam(CreateAction.PARAM_PROJECT_ID, viewUuid)
.setParam(CreateAction.PARAM_METRIC_ID, metric.getId().toString())
public void create_custom_measure_on_module() {
MetricDto metric = db.measures().insertMetric(m -> m.setUserManaged(true).setValueType(BOOL.name()));
ComponentDto project = db.components().insertPrivateProject();
ComponentDto module = db.components().insertComponent(newModuleDto(project));
UserDto user = db.users().insertUser();
userSession.logIn(user).addProjectPermission(ADMIN, project);

ws.newRequest()
.setParam(CreateAction.PARAM_PROJECT_ID, module.uuid())
.setParam(CreateAction.PARAM_METRIC_ID, Integer.toString(metric.getId()))
.setParam(CreateAction.PARAM_DESCRIPTION, "custom-measure-description")
.setParam(CreateAction.PARAM_VALUE, "true")
.execute();

List<CustomMeasureDto> customMeasures = dbClient.customMeasureDao().selectByMetricId(dbSession, metric.getId());
CustomMeasureDto customMeasure = customMeasures.get(0);
assertThat(customMeasures).hasSize(1);
assertThat(customMeasure.getComponentUuid()).isEqualTo(viewUuid);
assertThat(db.getDbClient().customMeasureDao().selectByMetricId(db.getSession(), metric.getId()))
.extracting(CustomMeasureDto::getDescription, CustomMeasureDto::getTextValue, CustomMeasureDto::getValue, CustomMeasureDto::getComponentUuid)
.containsExactlyInAnyOrder(tuple("custom-measure-description", null, 1d, module.uuid()));
}

@Test
public void create_custom_measure_on_a_sub_view() throws Exception {
String subViewUuid = "SUB_VIEW_UUID";

ComponentDto view = ComponentTesting.newView(db.organizations().insert());
dbClient.componentDao().insert(dbSession, view);
dbClient.componentDao().insert(dbSession, ComponentTesting.newSubView(view, subViewUuid, "SUB_VIEW_KEY"));
dbSession.commit();
MetricDto metric = insertMetric(BOOL);
userSession.logIn("login").addProjectPermission(UserRole.ADMIN, view);

newRequest()
.setParam(CreateAction.PARAM_PROJECT_ID, subViewUuid)
public void create_custom_measure_on_a_view() {
MetricDto metric = db.measures().insertMetric(m -> m.setUserManaged(true).setValueType(BOOL.name()));
OrganizationDto organization = db.organizations().insert();
ComponentDto view = db.components().insertPrivatePortfolio(organization);
UserDto user = db.users().insertUser();
userSession.logIn(user).addProjectPermission(ADMIN, view);

ws.newRequest()
.setParam(CreateAction.PARAM_PROJECT_ID, view.uuid())
.setParam(CreateAction.PARAM_METRIC_ID, metric.getId().toString())
.setParam(CreateAction.PARAM_DESCRIPTION, "custom-measure-description")
.setParam(CreateAction.PARAM_VALUE, "true")
.execute();

List<CustomMeasureDto> customMeasures = dbClient.customMeasureDao().selectByMetricId(dbSession, metric.getId());
CustomMeasureDto customMeasure = customMeasures.get(0);
assertThat(customMeasures).hasSize(1);
assertThat(customMeasure.getComponentUuid()).isEqualTo(subViewUuid);
assertThat(db.getDbClient().customMeasureDao().selectByMetricId(db.getSession(), metric.getId()))
.extracting(CustomMeasureDto::getDescription, CustomMeasureDto::getTextValue, CustomMeasureDto::getValue, CustomMeasureDto::getComponentUuid)
.containsExactlyInAnyOrder(tuple("custom-measure-description", null, 1d, view.uuid()));
}

@Test
public void fail_when_get_request() throws Exception {
expectedException.expect(ServerException.class);

ws.newGetRequest(CustomMeasuresWs.ENDPOINT, CreateAction.ACTION)
.setParam(CreateAction.PARAM_PROJECT_ID, DEFAULT_PROJECT_UUID)
.setParam(CreateAction.PARAM_METRIC_ID, "whatever-id")
.setParam(CreateAction.PARAM_VALUE, "custom-measure-free-text")
public void create_custom_measure_on_a_sub_view() {
MetricDto metric = db.measures().insertMetric(m -> m.setUserManaged(true).setValueType(BOOL.name()));
OrganizationDto organization = db.organizations().insert();
ComponentDto view = db.components().insertPrivatePortfolio(organization);
ComponentDto subView = db.components().insertSubView(view);
UserDto user = db.users().insertUser();
userSession.logIn(user).addProjectPermission(ADMIN, view);

ws.newRequest()
.setParam(CreateAction.PARAM_PROJECT_ID, subView.uuid())
.setParam(CreateAction.PARAM_METRIC_ID, metric.getId().toString())
.setParam(CreateAction.PARAM_DESCRIPTION, "custom-measure-description")
.setParam(CreateAction.PARAM_VALUE, "true")
.execute();

assertThat(db.getDbClient().customMeasureDao().selectByMetricId(db.getSession(), metric.getId()))
.extracting(CustomMeasureDto::getDescription, CustomMeasureDto::getTextValue, CustomMeasureDto::getValue, CustomMeasureDto::getComponentUuid)
.containsExactlyInAnyOrder(tuple("custom-measure-description", null, 1d, subView.uuid()));
}

@Test
public void fail_when_project_id_nor_project_key_provided() throws Exception {
public void fail_when_project_id_nor_project_key_provided() {
MetricDto metric = db.measures().insertMetric(m -> m.setUserManaged(true).setValueType(STRING.name()));
ComponentDto project = db.components().insertPrivateProject();
UserDto user = db.users().insertUser();
userSession.logIn(user).addProjectPermission(ADMIN, project);

expectedException.expect(IllegalArgumentException.class);
expectedException.expectMessage("Either 'projectId' or 'projectKey' must be provided");
MetricDto metric = insertMetric(STRING);

newRequest()
ws.newRequest()
.setParam(CreateAction.PARAM_METRIC_ID, "whatever-id")
.setParam(CreateAction.PARAM_VALUE, metric.getId().toString())
.execute();
}

@Test
public void fail_when_project_id_and_project_key_are_provided() throws Exception {
public void fail_when_project_id_and_project_key_are_provided() {
MetricDto metric = db.measures().insertMetric(m -> m.setUserManaged(true).setValueType(STRING.name()));
ComponentDto project = db.components().insertPrivateProject();
UserDto user = db.users().insertUser();
userSession.logIn(user).addProjectPermission(ADMIN, project);

expectedException.expect(IllegalArgumentException.class);
expectedException.expectMessage("Either 'projectId' or 'projectKey' must be provided");
MetricDto metric = insertMetric(STRING);

newRequest()
.setParam(CreateAction.PARAM_PROJECT_ID, DEFAULT_PROJECT_UUID)
.setParam(CreateAction.PARAM_PROJECT_KEY, DEFAULT_PROJECT_KEY)
ws.newRequest()
.setParam(CreateAction.PARAM_PROJECT_ID, project.uuid())
.setParam(CreateAction.PARAM_PROJECT_KEY, project.getKey())
.setParam(CreateAction.PARAM_METRIC_ID, metric.getId().toString())
.setParam(CreateAction.PARAM_VALUE, "whatever-value")
.execute();
}

@Test
public void fail_when_project_key_does_not_exist_in_db() throws Exception {
public void fail_when_project_key_does_not_exist_in_db() {
MetricDto metric = db.measures().insertMetric(m -> m.setUserManaged(true).setValueType(STRING.name()));
ComponentDto project = db.components().insertPrivateProject();
UserDto user = db.users().insertUser();
userSession.logIn(user).addProjectPermission(ADMIN, project);

expectedException.expect(NotFoundException.class);
expectedException.expectMessage("Component key 'another-project-key' not found");
insertMetric(STRING);

newRequest()
ws.newRequest()
.setParam(CreateAction.PARAM_PROJECT_KEY, "another-project-key")
.setParam(CreateAction.PARAM_METRIC_ID, "whatever-id")
.setParam(CreateAction.PARAM_METRIC_ID, metric.getId().toString())
.setParam(CreateAction.PARAM_VALUE, "whatever-value")
.execute();
}

@Test
public void fail_when_project_id_does_not_exist_in_db() throws Exception {
public void fail_when_project_id_does_not_exist_in_db() {
MetricDto metric = db.measures().insertMetric(m -> m.setUserManaged(true).setValueType(STRING.name()));
ComponentDto project = db.components().insertPrivateProject();
UserDto user = db.users().insertUser();
userSession.logIn(user).addProjectPermission(ADMIN, project);

expectedException.expect(NotFoundException.class);
expectedException.expectMessage("Component id 'another-project-uuid' not found");
MetricDto metric = insertMetric(STRING);

newRequest()
ws.newRequest()
.setParam(CreateAction.PARAM_PROJECT_ID, "another-project-uuid")
.setParam(CreateAction.PARAM_METRIC_ID, metric.getId().toString())
.setParam(CreateAction.PARAM_VALUE, "whatever-value")
@@ -359,25 +377,32 @@ public class CreateActionTest {
}

@Test
public void fail_when_metric_id_nor_metric_key_is_provided() throws Exception {
public void fail_when_metric_id_nor_metric_key_is_provided() {
ComponentDto project = db.components().insertPrivateProject();
UserDto user = db.users().insertUser();
userSession.logIn(user).addProjectPermission(ADMIN, project);

expectedException.expect(IllegalArgumentException.class);
expectedException.expectMessage("Either the metric id or the metric key must be provided");
insertMetric(STRING);

newRequest()
.setParam(CreateAction.PARAM_PROJECT_ID, DEFAULT_PROJECT_UUID)
ws.newRequest()
.setParam(CreateAction.PARAM_PROJECT_ID, project.uuid())
.setParam(CreateAction.PARAM_VALUE, "whatever-value")
.execute();
}

@Test
public void fail_when_metric_id_and_metric_key_are_provided() throws Exception {
public void fail_when_metric_id_and_metric_key_are_provided() {
MetricDto metric = db.measures().insertMetric(m -> m.setUserManaged(true).setValueType(STRING.name()));
ComponentDto project = db.components().insertPrivateProject();
UserDto user = db.users().insertUser();
userSession.logIn(user).addProjectPermission(ADMIN, project);

expectedException.expect(IllegalArgumentException.class);
expectedException.expectMessage("Either the metric id or the metric key must be provided");
MetricDto metric = insertMetric(STRING);

newRequest()
.setParam(CreateAction.PARAM_PROJECT_ID, DEFAULT_PROJECT_UUID)
ws.newRequest()
.setParam(CreateAction.PARAM_PROJECT_ID, project.uuid())
.setParam(CreateAction.PARAM_METRIC_ID, metric.getId().toString())
.setParam(CreateAction.PARAM_METRIC_KEY, metric.getKey())
.setParam(CreateAction.PARAM_VALUE, "whatever-value")
@@ -385,89 +410,104 @@ public class CreateActionTest {
}

@Test
public void fail_when_metric_is_not_found_in_db() throws Exception {
expectedException.expect(RowNotFoundException.class);
expectedException.expectMessage("Metric id '42' not found");
public void fail_when_metric_key_is_not_found_in_db() {
ComponentDto project = db.components().insertPrivateProject();
UserDto user = db.users().insertUser();
userSession.logIn(user).addProjectPermission(ADMIN, project);

newRequest()
.setParam(CreateAction.PARAM_PROJECT_ID, DEFAULT_PROJECT_UUID)
.setParam(CreateAction.PARAM_METRIC_ID, "42")
expectedException.expect(IllegalArgumentException.class);
expectedException.expectMessage("Metric with key 'unknown' does not exist");

ws.newRequest()
.setParam(CreateAction.PARAM_PROJECT_ID, project.uuid())
.setParam(CreateAction.PARAM_METRIC_KEY, "unknown")
.setParam(CreateAction.PARAM_VALUE, "whatever-value")
.execute();
}

@Test
public void fail_when_measure_already_exists_on_same_project_and_same_metric() throws Exception {
MetricDto metric = insertMetric(STRING);
public void fail_when_metric_id_is_not_found_in_db() {
ComponentDto project = db.components().insertPrivateProject();
UserDto user = db.users().insertUser();
userSession.logIn(user).addProjectPermission(ADMIN, project);

expectedException.expect(ServerException.class);
expectedException.expectMessage(String.format("A measure already exists for project 'project-key' (id: project-uuid) and metric 'metric-key' (id: '%d')", metric.getId()));
expectedException.expect(IllegalArgumentException.class);
expectedException.expectMessage("Metric with id '42' does not exist");

newRequest()
.setParam(CreateAction.PARAM_PROJECT_ID, DEFAULT_PROJECT_UUID)
.setParam(CreateAction.PARAM_METRIC_ID, metric.getId().toString())
ws.newRequest()
.setParam(CreateAction.PARAM_PROJECT_ID, project.uuid())
.setParam(CreateAction.PARAM_METRIC_ID, "42")
.setParam(CreateAction.PARAM_VALUE, "whatever-value")
.execute();
newRequest()
.setParam(CreateAction.PARAM_PROJECT_ID, DEFAULT_PROJECT_UUID)
}

@Test
public void fail_when_measure_already_exists_on_same_project_and_same_metric() {
MetricDto metric = db.measures().insertMetric(m -> m.setUserManaged(true).setValueType(STRING.name()));
ComponentDto project = db.components().insertPrivateProject();
UserDto userMeasureCreator = db.users().insertUser();
db.measures().insertCustomMeasure(userMeasureCreator, project, metric);
UserDto userAuthenticated = db.users().insertUser();
userSession.logIn(userAuthenticated).addProjectPermission(ADMIN, project);

expectedException.expect(BadRequestException.class);
expectedException.expectMessage(format("A measure already exists for project '%s' and metric '%s'", project.getKey(), metric.getKey()));

ws.newRequest()
.setParam(CreateAction.PARAM_PROJECT_ID, project.uuid())
.setParam(CreateAction.PARAM_METRIC_ID, metric.getId().toString())
.setParam(CreateAction.PARAM_VALUE, "whatever-value")
.execute();
}

@Test
public void fail_when_value_is_not_well_formatted() throws Exception {
MetricDto metric = insertMetric(BOOL);
public void fail_when_value_is_not_well_formatted() {
MetricDto metric = db.measures().insertMetric(m -> m.setUserManaged(true).setValueType(BOOL.name()));
ComponentDto project = db.components().insertPrivateProject();
UserDto user = db.users().insertUser();
userSession.logIn(user).addProjectPermission(ADMIN, project);

expectedException.expect(BadRequestException.class);
expectedException.expectMessage("Value 'non-correct-boolean-value' must be one of \"true\" or \"false\"");

newRequest()
.setParam(CreateAction.PARAM_PROJECT_ID, DEFAULT_PROJECT_UUID)
ws.newRequest()
.setParam(CreateAction.PARAM_PROJECT_ID, project.uuid())
.setParam(CreateAction.PARAM_METRIC_ID, metric.getId().toString())
.setParam(CreateAction.PARAM_VALUE, "non-correct-boolean-value")
.execute();
}

@Test
public void fail_when_system_administrator() throws Exception {
userSession.logIn().setSystemAdministrator().addPermission(OrganizationPermission.ADMINISTER, db.getDefaultOrganization());
MetricDto metric = insertMetric(STRING);
public void fail_when_system_administrator() {
MetricDto metric = db.measures().insertMetric(m -> m.setUserManaged(true).setValueType(BOOL.name()));
ComponentDto project = db.components().insertPrivateProject();
UserDto user = db.users().insertUser();
userSession.logIn(user).setSystemAdministrator();

expectedException.expect(ForbiddenException.class);

newRequest()
.setParam(CreateAction.PARAM_PROJECT_ID, DEFAULT_PROJECT_UUID)
ws.newRequest()
.setParam(CreateAction.PARAM_PROJECT_ID, project.uuid())
.setParam(CreateAction.PARAM_METRIC_ID, metric.getId().toString())
.setParam(CreateAction.PARAM_VALUE, "whatever-value")
.execute();
}

@Test
public void fail_when_not_a_project() throws Exception {
MetricDto metric = MetricTesting.newMetricDto().setEnabled(true).setValueType(STRING.name()).setKey("metric-key");
dbClient.metricDao().insert(dbSession, metric);
dbClient.componentDao().insert(dbSession, ComponentTesting.newDirectory(project, "directory-uuid", "path/to/directory").setDbKey("directory-key"));
dbSession.commit();
public void fail_when_not_a_project() {
MetricDto metric = db.measures().insertMetric(m -> m.setUserManaged(true).setValueType(BOOL.name()));
ComponentDto project = db.components().insertPrivateProject();
ComponentDto directory = db.components().insertComponent(ComponentTesting.newDirectory(project, "dir"));
UserDto user = db.users().insertUser();
userSession.logIn(user).addProjectPermission(ADMIN, project);

expectedException.expect(ServerException.class);
expectedException.expectMessage("Component 'directory-key' (id: directory-uuid) must be a project or a module.");
expectedException.expect(BadRequestException.class);
expectedException.expectMessage(format("Component '%s' must be a project or a module.", directory.getKey()));

newRequest()
.setParam(CreateAction.PARAM_PROJECT_ID, "directory-uuid")
ws.newRequest()
.setParam(CreateAction.PARAM_PROJECT_ID, directory.uuid())
.setParam(CreateAction.PARAM_METRIC_ID, metric.getId().toString())
.setParam(CreateAction.PARAM_VALUE, "whatever-value")
.execute();
}

private WsTester.TestRequest newRequest() {
return ws.newPostRequest(CustomMeasuresWs.ENDPOINT, CreateAction.ACTION);
}

private MetricDto insertMetric(ValueType metricType) {
MetricDto metric = MetricTesting.newMetricDto().setEnabled(true).setValueType(metricType.name()).setKey("metric-key");
dbClient.metricDao().insert(dbSession, metric);
dbSession.commit();
return metric;
}

}

+ 2
- 2
server/sonar-server/src/test/java/org/sonar/server/measure/custom/ws/DeleteActionTest.java View File

@@ -28,7 +28,6 @@ import org.sonar.api.web.UserRole;
import org.sonar.db.DbClient;
import org.sonar.db.DbSession;
import org.sonar.db.DbTester;
import org.sonar.db.RowNotFoundException;
import org.sonar.db.component.ComponentDto;
import org.sonar.db.measure.custom.CustomMeasureDto;
import org.sonar.server.exceptions.ForbiddenException;
@@ -74,7 +73,8 @@ public class DeleteActionTest {

@Test
public void throw_RowNotFoundException_if_id_does_not_exist() throws Exception {
expectedException.expect(RowNotFoundException.class);
expectedException.expect(IllegalArgumentException.class);
expectedException.expectMessage("Custom measure with id '42' does not exist");

newRequest().setParam(PARAM_ID, "42").execute();
}

+ 309
- 197
server/sonar-server/src/test/java/org/sonar/server/measure/custom/ws/SearchActionTest.java View File

@@ -19,150 +19,300 @@
*/
package org.sonar.server.measure.custom.ws;

import java.util.ArrayList;
import java.util.Date;
import org.apache.commons.lang.StringUtils;
import org.junit.Before;
import java.util.List;
import java.util.stream.IntStream;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
import org.sonar.api.measures.Metric.ValueType;
import org.sonar.api.server.ws.WebService;
import org.sonar.api.utils.DateUtils;
import org.sonar.api.utils.System2;
import org.sonar.api.web.UserRole;
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.component.ComponentTesting;
import org.sonar.db.component.SnapshotTesting;
import org.sonar.db.measure.custom.CustomMeasureDto;
import org.sonar.db.metric.MetricDto;
import org.sonar.db.user.UserDto;
import org.sonar.server.component.TestComponentFinder;
import org.sonar.server.es.EsTester;
import org.sonar.server.exceptions.ForbiddenException;
import org.sonar.server.exceptions.NotFoundException;
import org.sonar.server.tester.UserSessionRule;
import org.sonar.server.user.ws.UserJsonWriter;
import org.sonar.server.ws.WsTester;
import org.sonar.server.ws.WsActionTester;

import static org.assertj.core.api.Assertions.assertThat;
import static org.sonar.db.measure.custom.CustomMeasureTesting.newCustomMeasureDto;
import static org.sonar.db.metric.MetricTesting.newMetricDto;
import static org.sonar.api.measures.Metric.ValueType.STRING;
import static org.sonar.api.web.UserRole.ADMIN;
import static org.sonar.api.web.UserRole.USER;
import static org.sonar.test.JsonAssert.assertJson;

public class SearchActionTest {

private static final String DEFAULT_PROJECT_UUID = "project-uuid";
private static final String DEFAULT_PROJECT_KEY = "project-key";
private static final String METRIC_KEY_PREFIX = "metric-key-";

@Rule
public ExpectedException expectedException = ExpectedException.none();
@Rule
public UserSessionRule userSessionRule = UserSessionRule.standalone();
public UserSessionRule userSession = UserSessionRule.standalone();
@Rule
public DbTester db = DbTester.create(System2.INSTANCE);
@Rule
public EsTester es = EsTester.create();

private WsTester ws;
private DbClient dbClient = db.getDbClient();
private DbSession dbSession = db.getSession();
private ComponentDto defaultProject;

@Before
public void setUp() {
CustomMeasureJsonWriter customMeasureJsonWriter = new CustomMeasureJsonWriter(new UserJsonWriter(userSessionRule));
ws = new WsTester(new CustomMeasuresWs(new SearchAction(dbClient, customMeasureJsonWriter, userSessionRule, TestComponentFinder.from(db))));
defaultProject = insertDefaultProject();
userSessionRule.logIn().addProjectPermission(UserRole.ADMIN, defaultProject);
db.users().insertUser(u -> u.setLogin("login")
.setName("Login")
.setEmail("login@login.com")
.setActive(true));
}
private WsActionTester ws = new WsActionTester(
new SearchAction(db.getDbClient(), new CustomMeasureJsonWriter(new UserJsonWriter(userSession)), userSession, TestComponentFinder.from(db)));

@Test
public void json_well_formatted() throws Exception {
MetricDto metric1 = insertCustomMetric(1);
MetricDto metric2 = insertCustomMetric(2);
MetricDto metric3 = insertCustomMetric(3);
CustomMeasureDto customMeasure1 = insertCustomMeasure(1, metric1);
CustomMeasureDto customMeasure2 = insertCustomMeasure(2, metric2);
CustomMeasureDto customMeasure3 = insertCustomMeasure(3, metric3);

WsTester.Result response = newRequest()
.setParam(SearchAction.PARAM_PROJECT_ID, DEFAULT_PROJECT_UUID)
.execute();
public void json_well_formatted() {
ComponentDto project = db.components().insertPrivateProject();
UserDto userAuthenticated = db.users().insertUser();
userSession.logIn(userAuthenticated).addProjectPermission(ADMIN, project);
UserDto userMeasureCreator = db.users().insertUser();
MetricDto metric1 = db.measures().insertMetric(m -> m.setUserManaged(true).setValueType(STRING.name()));
MetricDto metric2 = db.measures().insertMetric(m -> m.setUserManaged(true).setValueType(STRING.name()));
MetricDto metric3 = db.measures().insertMetric(m -> m.setUserManaged(true).setValueType(STRING.name()));
CustomMeasureDto customMeasure1 = db.measures().insertCustomMeasure(userMeasureCreator, project, metric1, m -> m.setValue(0d));
CustomMeasureDto customMeasure2 = db.measures().insertCustomMeasure(userMeasureCreator, project, metric2, m -> m.setValue(0d));
CustomMeasureDto customMeasure3 = db.measures().insertCustomMeasure(userMeasureCreator, project, metric3, m -> m.setValue(0d));

String response = ws.newRequest()
.setParam(SearchAction.PARAM_PROJECT_ID, project.uuid())
.execute()
.getInput();

assertJson(response).isSimilarTo("{\n" +
" \"customMeasures\": [\n" +
" {\n" +
" \"id\": \"" + customMeasure1.getId() + "\",\n" +
" \"value\": \"" + customMeasure1.getTextValue() + "\",\n" +
" \"description\": \"" + customMeasure1.getDescription() + "\",\n" +
" \"metric\": {\n" +
" \"id\": \"" + metric1.getId() + "\",\n" +
" \"key\": \"" + metric1.getKey() + "\",\n" +
" \"type\": \"" + metric1.getValueType() + "\",\n" +
" \"name\": \"" + metric1.getShortName() + "\",\n" +
" \"domain\": \"" + metric1.getDomain() + "\"\n" +
" },\n" +
" \"projectId\": \"" + project.uuid() + "\",\n" +
" \"projectKey\": \"" + project.getKey() + "\",\n" +
" \"pending\": true,\n" +
" },\n" +
" {\n" +
" \"id\": \"" + customMeasure2.getId() + "\",\n" +
" \"value\": \"" + customMeasure2.getTextValue() + "\",\n" +
" \"description\": \"" + customMeasure2.getDescription() + "\",\n" +
" \"metric\": {\n" +
" \"id\": \"" + metric2.getId() + "\",\n" +
" \"key\": \"" + metric2.getKey() + "\",\n" +
" \"type\": \"" + metric2.getValueType() + "\",\n" +
" \"name\": \"" + metric2.getShortName() + "\",\n" +
" \"domain\": \"" + metric2.getDomain() + "\"\n" +
" },\n" +
" \"projectId\": \"" + project.uuid() + "\",\n" +
" \"projectKey\": \"" + project.getKey() + "\",\n" +
" \"pending\": true,\n" +
" },\n" +
" {\n" +
" \"id\": \"" + customMeasure3.getId() + "\",\n" +
" \"value\": \"" + customMeasure3.getTextValue() + "\",\n" +
" \"description\": \"" + customMeasure3.getDescription() + "\",\n" +
" \"metric\": {\n" +
" \"id\": \"" + metric3.getId() + "\",\n" +
" \"key\": \"" + metric3.getKey() + "\",\n" +
" \"type\": \"" + metric3.getValueType() + "\",\n" +
" \"name\": \"" + metric3.getShortName() + "\",\n" +
" \"domain\": \"" + metric3.getDomain() + "\"\n" +
" },\n" +
" \"projectId\": \"" + project.uuid() + "\",\n" +
" \"projectKey\": \"" + project.getKey() + "\",\n" +
" \"pending\": true,\n" +
" }\n" +
" ],\n" +
" \"total\": 3,\n" +
" \"p\": 1,\n" +
" \"ps\": 100\n" +
"}");
}

response.assertJson(getClass(), "custom-measures.json");
String responseAsString = response.outputAsString();
assertThat(responseAsString).matches(nameStringValuePattern("id", metric1.getId().toString()));
assertThat(responseAsString).matches(nameStringValuePattern("id", metric2.getId().toString()));
assertThat(responseAsString).matches(nameStringValuePattern("id", metric3.getId().toString()));
assertThat(responseAsString).matches(nameStringValuePattern("id", String.valueOf(customMeasure1.getId())));
assertThat(responseAsString).matches(nameStringValuePattern("id", String.valueOf(customMeasure2.getId())));
assertThat(responseAsString).matches(nameStringValuePattern("id", String.valueOf(customMeasure3.getId())));
assertThat(responseAsString).contains("createdAt", "updatedAt");
@Test
public void return_users() {
ComponentDto project = db.components().insertPrivateProject();
UserDto userAuthenticated = db.users().insertUser();
userSession.logIn(userAuthenticated).addProjectPermission(ADMIN, project);
UserDto user1 = db.users().insertUser();
UserDto user2 = db.users().insertUser();
MetricDto metric1 = db.measures().insertMetric(m -> m.setUserManaged(true).setValueType(STRING.name()));
MetricDto metric2 = db.measures().insertMetric(m -> m.setUserManaged(true).setValueType(STRING.name()));
MetricDto metric3 = db.measures().insertMetric(m -> m.setUserManaged(true).setValueType(STRING.name()));
CustomMeasureDto customMeasure1 = db.measures().insertCustomMeasure(user1, project, metric1, m -> m.setValue(0d));
CustomMeasureDto customMeasure2 = db.measures().insertCustomMeasure(user1, project, metric2, m -> m.setValue(0d));
CustomMeasureDto customMeasure3 = db.measures().insertCustomMeasure(user2, project, metric3, m -> m.setValue(0d));

String response = ws.newRequest()
.setParam(SearchAction.PARAM_PROJECT_ID, project.uuid())
.execute()
.getInput();

assertJson(response).isSimilarTo("{\n" +
" \"customMeasures\": [\n" +
" {\n" +
" \"id\": \"" + customMeasure1.getId() + "\",\n" +
" \"user\": {\n" +
" \"login\": \"" + user1.getLogin() +"\",\n" +
" \"name\": \"" + user1.getName() +"\",\n" +
" \"email\": \"" + user1.getEmail() +"\",\n" +
" \"active\": true\n" +
" }" +
" },\n" +
" {\n" +
" \"id\": \"" + customMeasure2.getId() + "\",\n" +
" \"user\": {\n" +
" \"login\": \"" + user1.getLogin() +"\",\n" +
" \"name\": \"" + user1.getName() +"\",\n" +
" \"email\": \"" + user1.getEmail() +"\",\n" +
" \"active\": true\n" +
" }" +
" },\n" +
" {\n" +
" \"id\": \"" + customMeasure3.getId() + "\",\n" +
" \"user\": {\n" +
" \"login\": \"" + user2.getLogin() +"\",\n" +
" \"name\": \"" + user2.getName() +"\",\n" +
" \"email\": \"" + user2.getEmail() +"\",\n" +
" \"active\": true\n" +
" }" +
" }\n" +
" ]\n" +
"}");
}

@Test
public void search_by_project_uuid() throws Exception {
MetricDto metric1 = insertCustomMetric(1);
MetricDto metric2 = insertCustomMetric(2);
MetricDto metric3 = insertCustomMetric(3);
insertCustomMeasure(1, metric1);
insertCustomMeasure(2, metric2);
insertCustomMeasure(3, metric3);

String response = newRequest()
.setParam(SearchAction.PARAM_PROJECT_ID, DEFAULT_PROJECT_UUID)
.execute().outputAsString();

assertThat(response).contains("text-value-1", "text-value-2", "text-value-3");
public void search_by_project_uuid() {
ComponentDto project = db.components().insertPrivateProject();
UserDto userAuthenticated = db.users().insertUser();
userSession.logIn(userAuthenticated).addProjectPermission(ADMIN, project);
UserDto userMeasureCreator = db.users().insertUser();
MetricDto metric1 = db.measures().insertMetric(m -> m.setUserManaged(true).setValueType(STRING.name()));
MetricDto metric2 = db.measures().insertMetric(m -> m.setUserManaged(true).setValueType(STRING.name()));
MetricDto metric3 = db.measures().insertMetric(m -> m.setUserManaged(true).setValueType(STRING.name()));
CustomMeasureDto customMeasure1 = db.measures().insertCustomMeasure(userMeasureCreator, project, metric1, m -> m.setValue(0d));
CustomMeasureDto customMeasure2 = db.measures().insertCustomMeasure(userMeasureCreator, project, metric2, m -> m.setValue(0d));
CustomMeasureDto customMeasure3 = db.measures().insertCustomMeasure(userMeasureCreator, project, metric3, m -> m.setValue(0d));

String response = ws.newRequest()
.setParam(SearchAction.PARAM_PROJECT_ID, project.uuid())
.execute()
.getInput();

assertJson(response).isSimilarTo("{\n" +
" \"customMeasures\": [\n" +
" {\n" +
" \"id\": \"" + customMeasure1.getId() + "\",\n" +
" \"value\": \"" + customMeasure1.getTextValue() + "\"\n" +
" },\n" +
" {\n" +
" \"id\": \"" + customMeasure2.getId() + "\",\n" +
" \"value\": \"" + customMeasure2.getTextValue() + "\"\n" +
" },\n" +
" {\n" +
" \"id\": \"" + customMeasure3.getId() + "\",\n" +
" \"value\": \"" + customMeasure3.getTextValue() + "\"\n" +
" }\n" +
" ],\n" +
" \"total\": 3,\n" +
" \"p\": 1,\n" +
" \"ps\": 100\n" +
"}");
}

@Test
public void search_by_project_key() throws Exception {
MetricDto metric1 = insertCustomMetric(1);
MetricDto metric2 = insertCustomMetric(2);
MetricDto metric3 = insertCustomMetric(3);
insertCustomMeasure(1, metric1);
insertCustomMeasure(2, metric2);
insertCustomMeasure(3, metric3);

String response = newRequest()
.setParam(SearchAction.PARAM_PROJECT_KEY, DEFAULT_PROJECT_KEY)
.execute().outputAsString();

assertThat(response).contains("text-value-1", "text-value-2", "text-value-3");
public void search_by_project_key() {
ComponentDto project = db.components().insertPrivateProject();
UserDto userAuthenticated = db.users().insertUser();
userSession.logIn(userAuthenticated).addProjectPermission(ADMIN, project);
UserDto userMeasureCreator = db.users().insertUser();
MetricDto metric1 = db.measures().insertMetric(m -> m.setUserManaged(true).setValueType(STRING.name()));
MetricDto metric2 = db.measures().insertMetric(m -> m.setUserManaged(true).setValueType(STRING.name()));
MetricDto metric3 = db.measures().insertMetric(m -> m.setUserManaged(true).setValueType(STRING.name()));
CustomMeasureDto customMeasure1 = db.measures().insertCustomMeasure(userMeasureCreator, project, metric1, m -> m.setValue(0d));
CustomMeasureDto customMeasure2 = db.measures().insertCustomMeasure(userMeasureCreator, project, metric2, m -> m.setValue(0d));
CustomMeasureDto customMeasure3 = db.measures().insertCustomMeasure(userMeasureCreator, project, metric3, m -> m.setValue(0d));

String response = ws.newRequest()
.setParam(SearchAction.PARAM_PROJECT_KEY, project.getKey())
.execute()
.getInput();

assertJson(response).isSimilarTo("{\n" +
" \"customMeasures\": [\n" +
" {\n" +
" \"id\": \"" + customMeasure1.getId() + "\",\n" +
" \"value\": \"" + customMeasure1.getTextValue() + "\"\n" +
" },\n" +
" {\n" +
" \"id\": \"" + customMeasure2.getId() + "\",\n" +
" \"value\": \"" + customMeasure2.getTextValue() + "\"\n" +
" },\n" +
" {\n" +
" \"id\": \"" + customMeasure3.getId() + "\",\n" +
" \"value\": \"" + customMeasure3.getTextValue() + "\"\n" +
" }\n" +
" ],\n" +
" \"total\": 3,\n" +
" \"p\": 1,\n" +
" \"ps\": 100\n" +
"}");
}

@Test
public void search_with_pagination() throws Exception {
for (int i = 0; i < 10; i++) {
MetricDto metric = insertCustomMetric(i);
insertCustomMeasure(i, metric);
}

String response = newRequest()
.setParam(SearchAction.PARAM_PROJECT_KEY, DEFAULT_PROJECT_KEY)
public void search_with_pagination() {
ComponentDto project = db.components().insertPrivateProject();
UserDto userAuthenticated = db.users().insertUser();
userSession.logIn(userAuthenticated).addProjectPermission(ADMIN, project);
UserDto userMeasureCreator = db.users().insertUser();
List<CustomMeasureDto> measureById = new ArrayList<>();
IntStream.range(0, 10).forEach(i -> {
MetricDto metric = db.measures().insertMetric(m -> m.setUserManaged(true));
CustomMeasureDto customMeasure = db.measures().insertCustomMeasure(userMeasureCreator, project, metric);
measureById.add(customMeasure);
});

String response = ws.newRequest()
.setParam(SearchAction.PARAM_PROJECT_KEY, project.getKey())
.setParam(WebService.Param.PAGE, "3")
.setParam(WebService.Param.PAGE_SIZE, "4")
.execute().outputAsString();

assertThat(StringUtils.countMatches(response, "text-value")).isEqualTo(2);
.execute()
.getInput();

assertJson(response).isSimilarTo("{\n" +
" \"customMeasures\": [\n" +
" {\n" +
" \"id\": \"" + measureById.get(8).getId() + "\",\n" +
" },\n" +
" {\n" +
" \"id\": \"" + measureById.get(9).getId() + "\",\n" +
" },\n" +
" ],\n" +
" \"total\": 10,\n" +
" \"p\": 3,\n" +
" \"ps\": 4\n" +
"}");
}

@Test
public void search_with_selectable_fields() throws Exception {
MetricDto metric = insertCustomMetric(1);
insertCustomMeasure(1, metric);

String response = newRequest()
.setParam(SearchAction.PARAM_PROJECT_KEY, DEFAULT_PROJECT_KEY)
public void search_with_selectable_fields() {
ComponentDto project = db.components().insertPrivateProject();
UserDto userAuthenticated = db.users().insertUser();
userSession.logIn(userAuthenticated).addProjectPermission(ADMIN, project);
UserDto userMeasureCreator = db.users().insertUser();
MetricDto metric = db.measures().insertMetric(m -> m.setUserManaged(true).setValueType(STRING.name()));
db.measures().insertCustomMeasure(userMeasureCreator, project, metric, m -> m.setValue(0d));

String response = ws.newRequest()
.setParam(SearchAction.PARAM_PROJECT_KEY, project.getKey())
.setParam(WebService.Param.FIELDS, "value, description")
.execute().outputAsString();
.execute()
.getInput();

assertThat(response).contains("id", "value", "description")
.doesNotContain("createdAt")
@@ -172,137 +322,99 @@ public class SearchActionTest {
}

@Test
public void search_with_more_recent_analysis() throws Exception {
public void search_with_more_recent_analysis() {
long yesterday = DateUtils.addDays(new Date(), -1).getTime();
MetricDto metric = insertCustomMetric(1);
dbClient.customMeasureDao().insert(dbSession, newCustomMeasure(1, metric)
.setCreatedAt(yesterday)
.setUpdatedAt(yesterday));
dbClient.snapshotDao().insert(dbSession, SnapshotTesting.newAnalysis(defaultProject));
dbSession.commit();

String response = newRequest()
.setParam(SearchAction.PARAM_PROJECT_ID, DEFAULT_PROJECT_UUID)
.execute().outputAsString();

assertThat(response).matches(nameValuePattern("pending", "false"));
ComponentDto project = db.components().insertPrivateProject();
UserDto userAuthenticated = db.users().insertUser();
userSession.logIn(userAuthenticated).addProjectPermission(ADMIN, project);
UserDto userMeasureCreator = db.users().insertUser();
MetricDto metric = db.measures().insertMetric(m -> m.setUserManaged(true).setValueType(STRING.name()));
CustomMeasureDto customMeasure = db.measures().insertCustomMeasure(userMeasureCreator, project, metric, m -> m.setCreatedAt(yesterday).setUpdatedAt(yesterday));
db.components().insertSnapshot(project);

String response = ws.newRequest()
.setParam(SearchAction.PARAM_PROJECT_ID, project.uuid())
.execute()
.getInput();

assertJson(response).isSimilarTo("{\n" +
" \"customMeasures\": [\n" +
" {\n" +
" \"id\": \"" + customMeasure.getId() + "\",\n" +
" \"value\": \"" + customMeasure.getTextValue() + "\",\n" +
" \"pending\": false\n" +
" },\n" +
" ]\n" +
"}");
}

@Test
public void search_as_project_admin() throws Exception {
userSessionRule.logIn("login").addProjectPermission(UserRole.ADMIN, defaultProject);
MetricDto metric1 = insertCustomMetric(1);
insertCustomMeasure(1, metric1);

String response = newRequest()
.setParam(SearchAction.PARAM_PROJECT_ID, DEFAULT_PROJECT_UUID)
.execute().outputAsString();

assertThat(response).contains("text-value-1");
public void empty_json_when_no_measure() {
ComponentDto project = db.components().insertPrivateProject();
UserDto userAuthenticated = db.users().insertUser();
userSession.logIn(userAuthenticated).addProjectPermission(ADMIN, project);

String response = ws.newRequest()
.setParam(SearchAction.PARAM_PROJECT_KEY, project.getKey())
.execute()
.getInput();

assertJson(response).isSimilarTo("{\n" +
" \"customMeasures\": [],\n" +
" \"total\": 0,\n" +
" \"p\": 1,\n" +
" \"ps\": 100\n" +
"}");
}

@Test
public void empty_json_when_no_measure() throws Exception {
WsTester.Result response = newRequest()
.setParam(SearchAction.PARAM_PROJECT_KEY, DEFAULT_PROJECT_KEY)
.execute();

response.assertJson(getClass(), "empty.json");
}
public void fail_when_project_id_and_project_key_provided() {
ComponentDto project = db.components().insertPrivateProject();
UserDto userAuthenticated = db.users().insertUser();
userSession.logIn(userAuthenticated).addProjectPermission(ADMIN, project);

@Test
public void fail_when_project_id_and_project_key_provided() throws Exception {
expectedException.expect(IllegalArgumentException.class);
expectedException.expectMessage("Either 'projectId' or 'projectKey' must be provided");

newRequest()
.setParam(SearchAction.PARAM_PROJECT_ID, DEFAULT_PROJECT_UUID)
.setParam(SearchAction.PARAM_PROJECT_KEY, DEFAULT_PROJECT_KEY)
ws.newRequest()
.setParam(SearchAction.PARAM_PROJECT_ID, project.uuid())
.setParam(SearchAction.PARAM_PROJECT_KEY, project.getKey())
.execute();
}

@Test
public void fail_when_project_id_nor_project_key_provided() throws Exception {
public void fail_when_project_id_nor_project_key_provided() {
expectedException.expect(IllegalArgumentException.class);
expectedException.expectMessage("Either 'projectId' or 'projectKey' must be provided");
newRequest().execute();

ws.newRequest()
.execute();
}

@Test
public void fail_when_project_not_found_in_db() throws Exception {
public void fail_when_project_not_found_in_db() {
expectedException.expect(NotFoundException.class);
expectedException.expectMessage("Component id 'wrong-project-uuid' not found");

newRequest().setParam(SearchAction.PARAM_PROJECT_ID, "wrong-project-uuid").execute();
ws.newRequest()
.setParam(SearchAction.PARAM_PROJECT_ID, "wrong-project-uuid")
.execute();
}

@Test
public void fail_when_not_enough_privileges() throws Exception {
public void fail_when_not_enough_privileges() {
ComponentDto project = db.components().insertPrivateProject();
UserDto userAuthenticated = db.users().insertUser();
userSession.logIn(userAuthenticated).addProjectPermission(USER, project);

expectedException.expect(ForbiddenException.class);
userSessionRule.logIn("login");
MetricDto metric1 = insertCustomMetric(1);
insertCustomMeasure(1, metric1);

String response = newRequest()
.setParam(SearchAction.PARAM_PROJECT_ID, DEFAULT_PROJECT_UUID)
.execute().outputAsString();
String response = ws.newRequest()
.setParam(SearchAction.PARAM_PROJECT_ID, project.uuid())
.execute()
.getInput();

assertThat(response).contains("text-value-1");
}

private ComponentDto insertDefaultProject() {
return insertProject(DEFAULT_PROJECT_UUID, DEFAULT_PROJECT_KEY);
}

private ComponentDto insertProject(String projectUuid, String projectKey) {
ComponentDto project = ComponentTesting.newPrivateProjectDto(db.organizations().insert(), projectUuid)
.setDbKey(projectKey);
dbClient.componentDao().insert(dbSession, project);
dbSession.commit();

return project;
}

private MetricDto insertCustomMetric(int id) {
MetricDto metric = newCustomMetric(METRIC_KEY_PREFIX + id);
dbClient.metricDao().insert(dbSession, metric);
dbSession.commit();

return metric;
}

private static MetricDto newCustomMetric(String metricKey) {
return newMetricDto().setEnabled(true).setUserManaged(true).setKey(metricKey).setDomain(metricKey + "-domain").setShortName(metricKey + "-name")
.setValueType(ValueType.STRING.name());
}

private CustomMeasureDto insertCustomMeasure(int id, MetricDto metric) {
CustomMeasureDto customMeasure = newCustomMeasure(id, metric);
dbClient.customMeasureDao().insert(dbSession, customMeasure);
dbSession.commit();

return customMeasure;
}

private CustomMeasureDto newCustomMeasure(int id, MetricDto metric) {
return newCustomMeasureDto()
.setUserLogin("login")
.setValue(id)
.setTextValue("text-value-" + id)
.setDescription("description-" + id)
.setMetricId(metric.getId())
.setComponentUuid(defaultProject.uuid());
}

private WsTester.TestRequest newRequest() {
return ws.newGetRequest(CustomMeasuresWs.ENDPOINT, SearchAction.ACTION);
}

private static String nameStringValuePattern(String name, String value) {
return String.format(".*\"%s\"\\s*:\\s*\"%s\".*", name, value);
}

private static String nameValuePattern(String name, String value) {
return String.format(".*\"%s\"\\s*:\\s*%s.*", name, value);
}
}

+ 140
- 201
server/sonar-server/src/test/java/org/sonar/server/measure/custom/ws/UpdateActionTest.java View File

@@ -19,246 +19,196 @@
*/
package org.sonar.server.measure.custom.ws;

import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
import org.sonar.api.measures.Metric.ValueType;
import org.sonar.api.utils.System2;
import org.sonar.api.web.UserRole;
import org.sonar.db.DbClient;
import org.sonar.db.DbSession;
import org.sonar.api.utils.internal.TestSystem2;
import org.sonar.db.DbTester;
import org.sonar.db.RowNotFoundException;
import org.sonar.db.component.ComponentDto;
import org.sonar.db.component.ComponentTesting;
import org.sonar.db.measure.custom.CustomMeasureDto;
import org.sonar.db.metric.MetricDto;
import org.sonar.db.metric.MetricTesting;
import org.sonar.db.organization.OrganizationDto;
import org.sonar.db.user.UserDto;
import org.sonar.server.es.EsTester;
import org.sonar.server.exceptions.ForbiddenException;
import org.sonar.server.exceptions.ServerException;
import org.sonar.server.exceptions.UnauthorizedException;
import org.sonar.server.tester.UserSessionRule;
import org.sonar.server.user.ws.UserJsonWriter;
import org.sonar.server.ws.WsTester;
import org.sonar.server.ws.WsActionTester;

import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.data.Offset.offset;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import static org.sonar.db.measure.custom.CustomMeasureTesting.newCustomMeasureDto;
import static org.assertj.core.api.Assertions.tuple;
import static org.sonar.api.measures.Metric.ValueType.INT;
import static org.sonar.api.measures.Metric.ValueType.STRING;
import static org.sonar.api.web.UserRole.ADMIN;
import static org.sonar.api.web.UserRole.USER;
import static org.sonar.server.measure.custom.ws.UpdateAction.PARAM_DESCRIPTION;
import static org.sonar.server.measure.custom.ws.UpdateAction.PARAM_ID;
import static org.sonar.server.measure.custom.ws.UpdateAction.PARAM_VALUE;
import static org.sonar.server.util.TypeValidationsTesting.newFullTypeValidations;
import static org.sonar.test.JsonAssert.assertJson;

public class UpdateActionTest {

private static final long NOW = 10_000_000_000L;

private System2 system = new TestSystem2().setNow(NOW);

@Rule
public ExpectedException expectedException = ExpectedException.none();
@Rule
public UserSessionRule userSessionRule = UserSessionRule.standalone();
public UserSessionRule userSession = UserSessionRule.standalone();
@Rule
public DbTester db = DbTester.create(System2.INSTANCE);
public DbTester db = DbTester.create();
@Rule
public EsTester es = EsTester.create();

private DbClient dbClient = db.getDbClient();
private DbSession dbSession = db.getSession();
private System2 system = mock(System2.class);
private WsTester ws;

@Before
public void setUp() {
CustomMeasureValidator validator = new CustomMeasureValidator(newFullTypeValidations());

ws = new WsTester(new CustomMeasuresWs(new UpdateAction(dbClient, userSessionRule, system, validator, new CustomMeasureJsonWriter(new UserJsonWriter(userSessionRule)))));

db.users().insertUser(u -> u.setLogin("login")
.setName("Login")
.setEmail("login@login.com")
.setActive(true)
);
dbSession.commit();
}
private WsActionTester ws = new WsActionTester(new UpdateAction(db.getDbClient(), userSession, system, new CustomMeasureValidator(newFullTypeValidations()),
new CustomMeasureJsonWriter(new UserJsonWriter(userSession))));

@Test
public void update_text_value_and_description_in_db() throws Exception {
MetricDto metric = insertNewMetric(ValueType.STRING);
ComponentDto component = db.components().insertPrivateProject(db.getDefaultOrganization(), "project-uuid");
CustomMeasureDto customMeasure = newCustomMeasure(component, metric)
.setDescription("custom-measure-description")
.setTextValue("text-measure-value");
dbClient.customMeasureDao().insert(dbSession, customMeasure);
dbSession.commit();
when(system.now()).thenReturn(123_456_789L);
logInAsProjectAdministrator(component);

ws.newPostRequest(CustomMeasuresWs.ENDPOINT, UpdateAction.ACTION)
public void update_text_value_and_description_in_db() {
ComponentDto project = db.components().insertPrivateProject();
UserDto userAuthenticated = db.users().insertUser();
userSession.logIn(userAuthenticated).addProjectPermission(ADMIN, project);
MetricDto metric = db.measures().insertMetric(m -> m.setUserManaged(true).setValueType(STRING.name()));
UserDto userMeasureCreator = db.users().insertUser();
CustomMeasureDto customMeasure = db.measures().insertCustomMeasure(userMeasureCreator, project, metric, m -> m.setValue(0d));

ws.newRequest()
.setParam(PARAM_ID, String.valueOf(customMeasure.getId()))
.setParam(PARAM_DESCRIPTION, "new-custom-measure-description")
.setParam(PARAM_VALUE, "new-text-measure-value")
.execute();
logInAsProjectAdministrator(component);

CustomMeasureDto updatedCustomMeasure = dbClient.customMeasureDao().selectOrFail(dbSession, customMeasure.getId());
assertThat(updatedCustomMeasure.getTextValue()).isEqualTo("new-text-measure-value");
assertThat(updatedCustomMeasure.getDescription()).isEqualTo("new-custom-measure-description");
assertThat(updatedCustomMeasure.getUpdatedAt()).isEqualTo(123_456_789L);
assertThat(customMeasure.getCreatedAt()).isEqualTo(updatedCustomMeasure.getCreatedAt());
assertThat(db.getDbClient().customMeasureDao().selectByMetricId(db.getSession(), metric.getId()))
.extracting(CustomMeasureDto::getDescription, CustomMeasureDto::getTextValue, CustomMeasureDto::getValue, CustomMeasureDto::getUserUuid, CustomMeasureDto::getComponentUuid,
CustomMeasureDto::getCreatedAt, CustomMeasureDto::getUpdatedAt)
.containsExactlyInAnyOrder(
tuple("new-custom-measure-description", "new-text-measure-value", 0d, userAuthenticated.getUuid(), project.uuid(), customMeasure.getCreatedAt(), NOW));
}

@Test
public void update_double_value_and_description_in_db() throws Exception {
MetricDto metric = insertNewMetric(ValueType.INT);
OrganizationDto organizationDto = db.organizations().insert();
ComponentDto component = db.components().insertPrivateProject(organizationDto, "project-uuid");
CustomMeasureDto customMeasure = newCustomMeasure(component, metric)
.setDescription("custom-measure-description")
.setValue(42d);
dbClient.customMeasureDao().insert(dbSession, customMeasure);
dbSession.commit();
logInAsProjectAdministrator(component);

ws.newPostRequest(CustomMeasuresWs.ENDPOINT, UpdateAction.ACTION)
public void update_int_value_and_description_in_db() {
ComponentDto project = db.components().insertPrivateProject();
UserDto userAuthenticated = db.users().insertUser();
userSession.logIn(userAuthenticated).addProjectPermission(ADMIN, project);
MetricDto metric = db.measures().insertMetric(m -> m.setUserManaged(true).setValueType(INT.name()));
UserDto userMeasureCreator = db.users().insertUser();
CustomMeasureDto customMeasure = db.measures().insertCustomMeasure(userMeasureCreator, project, metric, m -> m.setValue(42d).setTextValue(null));

ws.newRequest()
.setParam(PARAM_ID, String.valueOf(customMeasure.getId()))
.setParam(PARAM_DESCRIPTION, "new-custom-measure-description")
.setParam(PARAM_VALUE, "1984")
.execute();

CustomMeasureDto updatedCustomMeasure = dbClient.customMeasureDao().selectOrFail(dbSession, customMeasure.getId());
assertThat(updatedCustomMeasure.getValue()).isCloseTo(1984d, offset(0.01d));
assertThat(updatedCustomMeasure.getDescription()).isEqualTo("new-custom-measure-description");
assertThat(customMeasure.getCreatedAt()).isEqualTo(updatedCustomMeasure.getCreatedAt());
assertThat(db.getDbClient().customMeasureDao().selectByMetricId(db.getSession(), metric.getId()))
.extracting(CustomMeasureDto::getDescription, CustomMeasureDto::getTextValue, CustomMeasureDto::getValue, CustomMeasureDto::getUserUuid, CustomMeasureDto::getComponentUuid,
CustomMeasureDto::getCreatedAt, CustomMeasureDto::getUpdatedAt)
.containsExactlyInAnyOrder(
tuple("new-custom-measure-description", null, 1984d, userAuthenticated.getUuid(), project.uuid(), customMeasure.getCreatedAt(), NOW));
}

@Test
public void returns_full_object_in_response() throws Exception {
MetricDto metric = MetricTesting.newMetricDto().setEnabled(true)
.setValueType(ValueType.STRING.name())
.setKey("metric-key");
dbClient.metricDao().insert(dbSession, metric);
OrganizationDto organizationDto = db.organizations().insert();
ComponentDto component = ComponentTesting.newPrivateProjectDto(organizationDto, "project-uuid").setDbKey("project-key");
dbClient.componentDao().insert(dbSession, component);
CustomMeasureDto customMeasure = newCustomMeasure(component, metric)
.setCreatedAt(100_000_000L)
.setDescription("custom-measure-description")
.setTextValue("text-measure-value");
dbClient.customMeasureDao().insert(dbSession, customMeasure);
dbSession.commit();
when(system.now()).thenReturn(123_456_789L);
logInAsProjectAdministrator(component);

WsTester.Result response = ws.newPostRequest(CustomMeasuresWs.ENDPOINT, UpdateAction.ACTION)
public void returns_full_object_in_response() {
ComponentDto project = db.components().insertPrivateProject();
UserDto userAuthenticated = db.users().insertUser();
userSession.logIn(userAuthenticated).addProjectPermission(ADMIN, project);
MetricDto metric = db.measures().insertMetric(m -> m.setUserManaged(true).setValueType(STRING.name()));
UserDto userMeasureCreator = db.users().insertUser();
CustomMeasureDto customMeasure = db.measures().insertCustomMeasure(userMeasureCreator, project, metric, m -> m.setValue(0d).setCreatedAt(100_000_000L));

String response = ws.newRequest()
.setParam(PARAM_ID, String.valueOf(customMeasure.getId()))
.setParam(PARAM_DESCRIPTION, "new-custom-measure-description")
.setParam(PARAM_VALUE, "new-text-measure-value")
.execute();

response.assertJson(getClass(), "custom-measure.json");
String responseAsString = response.outputAsString();
assertThat(responseAsString).matches(String.format(".*\"id\"\\s*:\\s*\"%s\".*", customMeasure.getId()));
assertThat(responseAsString).matches(String.format(".*\"id\"\\s*:\\s*\"%s\".*", metric.getId()));
assertThat(responseAsString).matches(".*createdAt.*updatedAt.*");
.execute()
.getInput();

assertJson(response).isSimilarTo("{\n" +
" \"id\": \"" + customMeasure.getId() + "\",\n" +
" \"value\": \"new-text-measure-value\",\n" +
" \"description\": \"new-custom-measure-description\",\n" +
" \"metric\": {\n" +
" \"id\": \"" + metric.getId() + "\",\n" +
" \"key\": \"" + metric.getKey() + "\",\n" +
" \"type\": \"" + metric.getValueType() + "\",\n" +
" \"name\": \"" + metric.getShortName() + "\",\n" +
" \"domain\": \"" + metric.getDomain() + "\"\n" +
" },\n" +
" \"projectId\": \"" + project.uuid() + "\",\n" +
" \"projectKey\": \"" + project.getKey() + "\",\n" +
"}");
}

@Test
public void update_value_only() throws Exception {
MetricDto metric = insertNewMetric(ValueType.STRING);
ComponentDto component = db.components().insertPrivateProject(db.getDefaultOrganization(), "project-uuid");
CustomMeasureDto customMeasure = newCustomMeasure(component, metric)
.setDescription("custom-measure-description")
.setTextValue("text-measure-value");
dbClient.customMeasureDao().insert(dbSession, customMeasure);
dbSession.commit();
when(system.now()).thenReturn(123_456_789L);
logInAsProjectAdministrator(component);

ws.newPostRequest(CustomMeasuresWs.ENDPOINT, UpdateAction.ACTION)
public void update_description_only() {
ComponentDto project = db.components().insertPrivateProject();
UserDto user = db.users().insertUser();
userSession.logIn(user).addProjectPermission(ADMIN, project);
MetricDto metric = db.measures().insertMetric(m -> m.setUserManaged(true).setValueType(STRING.name()));
CustomMeasureDto customMeasure = db.measures().insertCustomMeasure(user, project, metric);

ws.newRequest()
.setParam(PARAM_ID, String.valueOf(customMeasure.getId()))
.setParam(PARAM_DESCRIPTION, "new-custom-measure-description")
.execute();
logInAsProjectAdministrator(component);

CustomMeasureDto updatedCustomMeasure = dbClient.customMeasureDao().selectOrFail(dbSession, customMeasure.getId());
assertThat(updatedCustomMeasure.getTextValue()).isEqualTo("text-measure-value");
assertThat(updatedCustomMeasure.getDescription()).isEqualTo("new-custom-measure-description");
assertThat(updatedCustomMeasure.getUpdatedAt()).isEqualTo(123_456_789L);
assertThat(customMeasure.getCreatedAt()).isEqualTo(updatedCustomMeasure.getCreatedAt());
assertThat(db.getDbClient().customMeasureDao().selectByMetricId(db.getSession(), metric.getId()))
.extracting(CustomMeasureDto::getDescription, CustomMeasureDto::getTextValue, CustomMeasureDto::getValue)
.containsExactlyInAnyOrder(
tuple("new-custom-measure-description", customMeasure.getTextValue(), customMeasure.getValue()));
}

@Test
public void update_description_only() throws Exception {
MetricDto metric = insertNewMetric(ValueType.STRING);
OrganizationDto organizationDto = db.organizations().insert();
ComponentDto component = db.components().insertPrivateProject(organizationDto, "project-uuid");
CustomMeasureDto customMeasure = newCustomMeasure(component, metric)
.setMetricId(metric.getId())
.setComponentUuid(component.uuid())
.setCreatedAt(system.now())
.setDescription("custom-measure-description")
.setTextValue("text-measure-value");
dbClient.customMeasureDao().insert(dbSession, customMeasure);
dbSession.commit();
when(system.now()).thenReturn(123_456_789L);
logInAsProjectAdministrator(component);

ws.newPostRequest(CustomMeasuresWs.ENDPOINT, UpdateAction.ACTION)
public void update_value_only() {
ComponentDto project = db.components().insertPrivateProject();
UserDto user = db.users().insertUser();
userSession.logIn(user).addProjectPermission(ADMIN, project);
MetricDto metric = db.measures().insertMetric(m -> m.setUserManaged(true).setValueType(STRING.name()));
CustomMeasureDto customMeasure = db.measures().insertCustomMeasure(user, project, metric);

ws.newRequest()
.setParam(PARAM_ID, String.valueOf(customMeasure.getId()))
.setParam(PARAM_VALUE, "new-text-measure-value")
.execute();

CustomMeasureDto updatedCustomMeasure = dbClient.customMeasureDao().selectOrFail(dbSession, customMeasure.getId());
assertThat(updatedCustomMeasure.getTextValue()).isEqualTo("new-text-measure-value");
assertThat(updatedCustomMeasure.getDescription()).isEqualTo("custom-measure-description");
assertThat(updatedCustomMeasure.getUpdatedAt()).isEqualTo(123_456_789L);
assertThat(customMeasure.getCreatedAt()).isEqualTo(updatedCustomMeasure.getCreatedAt());
assertThat(db.getDbClient().customMeasureDao().selectByMetricId(db.getSession(), metric.getId()))
.extracting(CustomMeasureDto::getDescription, CustomMeasureDto::getTextValue, CustomMeasureDto::getValue)
.containsExactlyInAnyOrder(
tuple(customMeasure.getDescription(), "new-text-measure-value", customMeasure.getValue()));
}

@Test
public void fail_if_get_request() throws Exception {
expectedException.expect(ServerException.class);

ws.newGetRequest(CustomMeasuresWs.ENDPOINT, UpdateAction.ACTION)
.setParam(PARAM_ID, "42")
.setParam(PARAM_DESCRIPTION, "new-custom-measure-description")
.setParam(PARAM_VALUE, "1984")
.execute();
}
public void fail_if_measure_is_not_in_db() {
ComponentDto project = db.components().insertPrivateProject();
MetricDto metric = db.measures().insertMetric(m -> m.setUserManaged(true).setValueType(STRING.name()));
UserDto user = db.users().insertUser();
db.measures().insertCustomMeasure(user, project, metric);
userSession.logIn(user).addProjectPermission(ADMIN, project);

@Test
public void fail_if_not_in_db() throws Exception {
expectedException.expect(RowNotFoundException.class);
expectedException.expectMessage("Custom measure '42' not found.");
expectedException.expect(IllegalArgumentException.class);
expectedException.expectMessage("Custom measure with id '0' does not exist");

ws.newPostRequest(CustomMeasuresWs.ENDPOINT, UpdateAction.ACTION)
.setParam(PARAM_ID, "42")
ws.newRequest()
.setParam(PARAM_ID, "0")
.setParam(PARAM_DESCRIPTION, "new-custom-measure-description")
.setParam(PARAM_VALUE, "1984")
.execute();
}

@Test
public void fail_if_insufficient_privileges() throws Exception {
userSessionRule.logIn();
MetricDto metric = MetricTesting.newMetricDto().setEnabled(true).setValueType(ValueType.STRING.name());
dbClient.metricDao().insert(dbSession, metric);
ComponentDto component = ComponentTesting.newPrivateProjectDto(db.getDefaultOrganization(), "project-uuid");
dbClient.componentDao().insert(dbSession, component);
CustomMeasureDto customMeasure = newCustomMeasureDto()
.setMetricId(metric.getId())
.setComponentUuid(component.uuid())
.setCreatedAt(system.now())
.setDescription("custom-measure-description")
.setTextValue("text-measure-value");
dbClient.customMeasureDao().insert(dbSession, customMeasure);
dbSession.commit();
public void fail_if_insufficient_privileges() {
ComponentDto project = db.components().insertPrivateProject();
MetricDto metric = db.measures().insertMetric(m -> m.setUserManaged(true).setValueType(STRING.name()));
UserDto user = db.users().insertUser();
CustomMeasureDto customMeasure = db.measures().insertCustomMeasure(user, project, metric);
userSession.logIn(user).addProjectPermission(USER, project);

expectedException.expect(ForbiddenException.class);

ws.newPostRequest(CustomMeasuresWs.ENDPOINT, UpdateAction.ACTION)
ws.newRequest()
.setParam(PARAM_ID, String.valueOf(customMeasure.getId()))
.setParam(PARAM_DESCRIPTION, "new-custom-measure-description")
.setParam(PARAM_VALUE, "1984")
@@ -266,24 +216,16 @@ public class UpdateActionTest {
}

@Test
public void fail_if_not_logged_in() throws Exception {
userSessionRule.anonymous();
public void fail_if_not_logged_in() {
ComponentDto project = db.components().insertPrivateProject();
UserDto user = db.users().insertUser();
MetricDto metric = db.measures().insertMetric(m -> m.setUserManaged(true).setValueType(STRING.name()));
CustomMeasureDto customMeasure = db.measures().insertCustomMeasure(user, project, metric);
userSession.anonymous();

expectedException.expect(UnauthorizedException.class);
MetricDto metric = MetricTesting.newMetricDto().setEnabled(true).setValueType(ValueType.STRING.name());
dbClient.metricDao().insert(dbSession, metric);
OrganizationDto organizationDto = db.organizations().insert();
ComponentDto component = ComponentTesting.newPrivateProjectDto(organizationDto, "project-uuid");
dbClient.componentDao().insert(dbSession, component);
CustomMeasureDto customMeasure = newCustomMeasureDto()
.setMetricId(metric.getId())
.setComponentUuid(component.uuid())
.setCreatedAt(system.now())
.setDescription("custom-measure-description")
.setTextValue("text-measure-value");
dbClient.customMeasureDao().insert(dbSession, customMeasure);
dbSession.commit();

ws.newPostRequest(CustomMeasuresWs.ENDPOINT, UpdateAction.ACTION)

ws.newRequest()
.setParam(PARAM_ID, String.valueOf(customMeasure.getId()))
.setParam(PARAM_DESCRIPTION, "new-custom-measure-description")
.setParam(PARAM_VALUE, "1984")
@@ -291,39 +233,36 @@ public class UpdateActionTest {
}

@Test
public void fail_if_custom_measure_id_is_missing_in_request() throws Exception {
public void fail_if_custom_measure_id_is_missing_in_request() {
ComponentDto project = db.components().insertPrivateProject();
MetricDto metric = db.measures().insertMetric(m -> m.setUserManaged(true).setValueType(STRING.name()));
UserDto user = db.users().insertUser();
db.measures().insertCustomMeasure(user, project, metric);
userSession.logIn(user).addProjectPermission(ADMIN, project);

expectedException.expect(IllegalArgumentException.class);
expectedException.expectMessage("The 'id' parameter is missing");

ws.newPostRequest(CustomMeasuresWs.ENDPOINT, UpdateAction.ACTION)
ws.newRequest()
.setParam(PARAM_DESCRIPTION, "new-custom-measure-description")
.setParam(PARAM_VALUE, "1984")
.execute();
}

@Test
public void fail_if_custom_measure_value_and_description_are_missing_in_request() throws Exception {
public void fail_if_custom_measure_value_and_description_are_missing_in_request() {
ComponentDto project = db.components().insertPrivateProject();
MetricDto metric = db.measures().insertMetric(m -> m.setUserManaged(true).setValueType(STRING.name()));
UserDto user = db.users().insertUser();
db.measures().insertCustomMeasure(user, project, metric);
userSession.logIn(user).addProjectPermission(ADMIN, project);

expectedException.expect(IllegalArgumentException.class);
expectedException.expectMessage("Value or description must be provided.");

ws.newPostRequest(CustomMeasuresWs.ENDPOINT, UpdateAction.ACTION)
ws.newRequest()
.setParam(PARAM_ID, "42")
.execute();
}

private MetricDto insertNewMetric(ValueType metricType) {
MetricDto metric = MetricTesting.newMetricDto().setEnabled(true).setValueType(metricType.name());
dbClient.metricDao().insert(dbSession, metric);

return metric;
}

private CustomMeasureDto newCustomMeasure(ComponentDto project, MetricDto metric) {
return newCustomMeasureDto()
.setMetricId(metric.getId())
.setComponentUuid(project.uuid())
.setCreatedAt(system.now());
}

private void logInAsProjectAdministrator(ComponentDto component) {
userSessionRule.logIn("login").addProjectPermission(UserRole.ADMIN, component);
}
}

+ 4
- 4
server/sonar-server/src/test/java/org/sonar/server/user/ws/ChangePasswordActionTest.java View File

@@ -84,7 +84,7 @@ public class ChangePasswordActionTest {
.setPassword("Valar Dohaeris")
.build(), u -> {
});
String oldCryptedPassword = db.getDbClient().userDao().selectOrFailByLogin(db.getSession(), "john").getCryptedPassword();
String oldCryptedPassword = db.getDbClient().userDao().selectByLogin(db.getSession(), "john").getCryptedPassword();
userSessionRule.logIn("john");

TestResponse response = tester.newRequest()
@@ -94,7 +94,7 @@ public class ChangePasswordActionTest {
.execute();

assertThat(response.getStatus()).isEqualTo(204);
String newCryptedPassword = db.getDbClient().userDao().selectOrFailByLogin(db.getSession(), "john").getCryptedPassword();
String newCryptedPassword = db.getDbClient().userDao().selectByLogin(db.getSession(), "john").getCryptedPassword();
assertThat(newCryptedPassword).isNotEqualTo(oldCryptedPassword);
}

@@ -102,14 +102,14 @@ public class ChangePasswordActionTest {
public void system_administrator_can_update_password_of_user() {
userSessionRule.logIn().setSystemAdministrator();
createLocalUser();
String originalPassword = db.getDbClient().userDao().selectOrFailByLogin(db.getSession(), "john").getCryptedPassword();
String originalPassword = db.getDbClient().userDao().selectByLogin(db.getSession(), "john").getCryptedPassword();

tester.newRequest()
.setParam("login", "john")
.setParam("password", "Valar Morghulis")
.execute();

String newPassword = db.getDbClient().userDao().selectOrFailByLogin(db.getSession(), "john").getCryptedPassword();
String newPassword = db.getDbClient().userDao().selectByLogin(db.getSession(), "john").getCryptedPassword();
assertThat(newPassword).isNotEqualTo(originalPassword);
}


+ 0
- 17
server/sonar-server/src/test/resources/org/sonar/server/measure/custom/ws/CreateActionTest/custom-measure.json View File

@@ -1,17 +0,0 @@
{
"projectId": "project-uuid",
"projectKey": "project-key",
"metric": {
"key": "metric-key",
"type": "STRING"
},
"value": "custom-measure-free-text",
"description": "custom-measure-description",
"pending":true,
"user": {
"active": true,
"email": "login@login.com",
"login": "login",
"name": "Login"
}
}

+ 64
- 0
tests/src/test/java/org/sonarqube/tests/user/SonarCloudUpdateLoginDuringAuthenticationTest.java View File

@@ -34,21 +34,25 @@ import org.sonarqube.ws.Issues;
import org.sonarqube.ws.Issues.Issue;
import org.sonarqube.ws.Organizations.Organization;
import org.sonarqube.ws.Projects;
import org.sonarqube.ws.Projects.CreateWsResponse.Project;
import org.sonarqube.ws.Qualityprofiles;
import org.sonarqube.ws.Settings;
import org.sonarqube.ws.UserTokens;
import org.sonarqube.ws.Users;
import org.sonarqube.ws.client.GetRequest;
import org.sonarqube.ws.client.WsClient;
import org.sonarqube.ws.client.custommeasures.CreateRequest;
import org.sonarqube.ws.client.issues.AssignRequest;
import org.sonarqube.ws.client.organizations.AddMemberRequest;
import org.sonarqube.ws.client.organizations.SearchRequest;
import org.sonarqube.ws.client.permissions.AddUserRequest;
import org.sonarqube.ws.client.qualityprofiles.ChangelogRequest;
import org.sonarqube.ws.client.settings.SetRequest;
import org.sonarqube.ws.client.settings.ValuesRequest;
import org.sonarqube.ws.client.usertokens.GenerateRequest;

import static java.util.Collections.singletonList;
import static org.apache.commons.lang.RandomStringUtils.randomAlphanumeric;
import static org.assertj.core.api.Assertions.assertThat;
import static org.skyscreamer.jsonassert.JSONAssert.assertEquals;
import static util.ItUtils.projectDir;
@@ -262,6 +266,66 @@ public class SonarCloudUpdateLoginDuringAuthenticationTest {
.contains("token1", "token2");
}

@Test
public void manual_measure_after_login_update() throws JSONException {
tester.settings().setGlobalSettings("sonar.organizations.anyoneCanCreate", "true");
String providerId = tester.users().generateProviderId();
String oldLogin = tester.users().generateLogin();

// Create user using authentication
authenticate(oldLogin, providerId);
String userToken = tester.wsClient().userTokens().generate(new GenerateRequest().setLogin(oldLogin).setName("token")).getToken();
WsClient userWsClient = tester.as(userToken, null).wsClient();

// Grant user the admin permission on a project
Organization organization = tester.organizations().generate();
Project project = tester.projects().provision(organization);
tester.organizations().service().addMember(new AddMemberRequest().setOrganization(organization.getKey()).setLogin(oldLogin));
String customMetricKey = randomAlphanumeric(50);
tester.wsClient().metrics().create(new org.sonarqube.ws.client.metrics.CreateRequest().setKey(customMetricKey).setName("custom").setType("INT"));
tester.wsClient().permissions().addUser(new AddUserRequest().setLogin(oldLogin).setProjectKey(project.getKey()).setPermission("admin"));

// Create a manual metric and a manual measure on it
userWsClient.customMeasures().create(new CreateRequest().setMetricKey(customMetricKey).setProjectKey(project.getKey()).setValue("50"));
String manualMeasures = tester.wsClient().customMeasures().search(new org.sonarqube.ws.client.custommeasures.SearchRequest().setProjectKey(project.getKey()));
assertEquals(
"{\n" +
" \"customMeasures\": [\n" +
" {\n" +
" \"projectKey\": \"" + project.getKey() + "\",\n" +
" \"user\": {\n" +
" \"login\": \"" + oldLogin + "\",\n" +
" \"name\": \"John\",\n" +
" \"email\": \"john@email.com\",\n" +
" \"active\": true\n" +
" }" +
" }" +
" ]\n" +
"}",
manualMeasures,
false);

// Update login during authentication, check manual measure contains new user login
String newLogin = tester.users().generateLogin();
authenticate(newLogin, providerId);
assertEquals(
"{\n" +
" \"customMeasures\": [\n" +
" {\n" +
" \"projectKey\": \"" + project.getKey() + "\",\n" +
" \"user\": {\n" +
" \"login\": \"" + newLogin + "\",\n" +
" \"name\": \"John\",\n" +
" \"email\": \"john@email.com\",\n" +
" \"active\": true\n" +
" }" +
" }" +
" ]\n" +
"}",
tester.wsClient().customMeasures().search(new org.sonarqube.ws.client.custommeasures.SearchRequest().setProjectKey(project.getKey())),
false);
}

private void authenticate(String login, String providerId) {
tester.settings().setGlobalSettings("sonar.auth.fake-base-id-provider.user", login + "," + providerId + ",fake-" + login + ",John,john@email.com");
tester.wsClient().wsConnector().call(

Loading…
Cancel
Save