@CheckForNull
public CharacteristicDto selectByKey(String key) {
SqlSession session = mybatis.openSession();
- CharacteristicMapper mapper = session.getMapper(CharacteristicMapper.class);
try {
- return mapper.selectByKey(key);
+ return selectByKey(key, session);
} finally {
MyBatis.closeQuietly(session);
}
}
+ @CheckForNull
+ public CharacteristicDto selectByKey(String key, SqlSession session) {
+ return session.getMapper(CharacteristicMapper.class).selectByKey(key);
+ }
+
@CheckForNull
public CharacteristicDto selectById(int id) {
SqlSession session = mybatis.openSession();
- CharacteristicMapper mapper = session.getMapper(CharacteristicMapper.class);
try {
- return mapper.selectById(id);
+ return selectById(id, session);
} finally {
MyBatis.closeQuietly(session);
}
}
+ @CheckForNull
+ public CharacteristicDto selectById(int id, SqlSession session) {
+ return session.getMapper(CharacteristicMapper.class).selectById(id);
+ }
+
@CheckForNull
public CharacteristicDto selectByName(String name) {
SqlSession session = mybatis.openSession();
return session.getMapper(CharacteristicMapper.class).selectByName(name);
}
+ @CheckForNull
+ public CharacteristicDto selectNext(int order, SqlSession session) {
+ List<CharacteristicDto> dtos = session.getMapper(CharacteristicMapper.class).selectNext(order);
+ return dtos.isEmpty() ? null : dtos.get(0);
+ }
+
+ @CheckForNull
+ public CharacteristicDto selectNext(int order) {
+ SqlSession session = mybatis.openSession();
+ try {
+ return selectNext(order, session);
+ } finally {
+ MyBatis.closeQuietly(session);
+ }
+ }
+
+ @CheckForNull
+ public CharacteristicDto selectPrevious(int order, SqlSession session) {
+ List<CharacteristicDto> dtos = session.getMapper(CharacteristicMapper.class).selectPrevious(order);
+ return dtos.isEmpty() ? null : dtos.get(0);
+ }
+
+ @CheckForNull
+ public CharacteristicDto selectPrevious(int order) {
+ SqlSession session = mybatis.openSession();
+ try {
+ return selectPrevious(order, session);
+ } finally {
+ MyBatis.closeQuietly(session);
+ }
+ }
+
public int selectMaxCharacteristicOrder() {
SqlSession session = mybatis.openSession();
try {
import org.sonar.api.server.debt.DebtCharacteristic;
import org.sonar.api.server.debt.DebtModel;
import org.sonar.api.server.debt.internal.DefaultDebtCharacteristic;
+import org.sonar.core.permission.GlobalPermissions;
import org.sonar.core.persistence.MyBatis;
import org.sonar.core.technicaldebt.db.CharacteristicDao;
import org.sonar.core.technicaldebt.db.CharacteristicDto;
import org.sonar.server.exceptions.BadRequestException;
import org.sonar.server.exceptions.NotFoundException;
+import org.sonar.server.user.UserSession;
import org.sonar.server.util.Validation;
import javax.annotation.CheckForNull;
return dto != null ? toCharacteristic(dto) : null;
}
- public DebtCharacteristic createCharacteristic(String name, @Nullable Integer parentId) {
+ public DebtCharacteristic create(String name, @Nullable Integer parentId) {
+ checkPermission();
+
SqlSession session = mybatis.openSession();
try {
checkNotAlreadyExists(name, session);
// New sub characteristic
if (parentId != null) {
- CharacteristicDto parent = findCharacteristic(parentId);
+ CharacteristicDto parent = findCharacteristic(parentId, session);
if (parent.getParentId() != null) {
throw new BadRequestException("A sub characteristic can not have a sub characteristic as parent.");
}
newCharacteristic.setParentId(parent.getId());
} else {
// New root characteristic
- newCharacteristic.setOrder(dao.selectMaxCharacteristicOrder(session)+1);
+ newCharacteristic.setOrder(dao.selectMaxCharacteristicOrder(session) + 1);
}
dao.insert(newCharacteristic, session);
session.commit();
}
}
- private CharacteristicDto findCharacteristic(Integer id){
- CharacteristicDto dto = dao.selectById(id);
+ public DebtCharacteristic rename(int characteristicId, String newName) {
+ checkPermission();
+
+ SqlSession session = mybatis.openSession();
+ try {
+ checkNotAlreadyExists(newName, session);
+
+ CharacteristicDto dto = findCharacteristic(characteristicId, session);
+ if (!dto.getName().equals(newName)) {
+ dto.setName(newName);
+ dao.update(dto, session);
+ session.commit();
+ }
+ return toCharacteristic(dto);
+ } finally {
+ MyBatis.closeQuietly(session);
+ }
+ }
+
+ public DebtCharacteristic moveUp(int characteristicId) {
+ return move(characteristicId, true);
+ }
+
+ public DebtCharacteristic moveDown(int characteristicId) {
+ return move(characteristicId, false);
+ }
+
+ private DebtCharacteristic move(int characteristicId, boolean moveUpOrDown) {
+ checkPermission();
+
+ SqlSession session = mybatis.openSession();
+ try {
+ CharacteristicDto dto = findCharacteristic(characteristicId, session);
+ int currentOrder = dto.getOrder();
+ CharacteristicDto dtoToSwitchOrderWith = moveUpOrDown ? dao.selectPrevious(currentOrder, session) : dao.selectNext(currentOrder, session);
+
+ // Do nothing when characteristic is already to the new location
+ if (dtoToSwitchOrderWith == null) {
+ return toCharacteristic(dto);
+ }
+ int nextOrder = dtoToSwitchOrderWith.getOrder();
+ dtoToSwitchOrderWith.setOrder(currentOrder);
+ dao.update(dtoToSwitchOrderWith, session);
+
+ dto.setOrder(nextOrder);
+ dao.update(dto, session);
+
+ session.commit();
+ return toCharacteristic(dto);
+ } finally {
+ MyBatis.closeQuietly(session);
+ }
+ }
+
+ private CharacteristicDto findCharacteristic(Integer id, SqlSession session) {
+ CharacteristicDto dto = dao.selectById(id, session);
if (dto == null) {
throw new NotFoundException(String.format("Characteristic with id %s does not exists.", id));
}
}
}
+ private void checkPermission() {
+ UserSession.get().checkGlobalPermission(GlobalPermissions.SYSTEM_ADMIN);
+ }
+
private static List<DebtCharacteristic> toCharacteristics(Collection<CharacteristicDto> dtos) {
return newArrayList(Iterables.transform(dtos, new Function<CharacteristicDto, DebtCharacteristic>() {
@Override
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
import org.mockito.invocation.InvocationOnMock;
import org.mockito.runners.MockitoJUnitRunner;
import org.mockito.stubbing.Answer;
import org.sonar.api.server.debt.DebtCharacteristic;
+import org.sonar.core.permission.GlobalPermissions;
import org.sonar.core.persistence.MyBatis;
import org.sonar.core.technicaldebt.db.CharacteristicDao;
import org.sonar.core.technicaldebt.db.CharacteristicDto;
import org.sonar.server.exceptions.BadRequestException;
+import org.sonar.server.exceptions.ForbiddenException;
import org.sonar.server.exceptions.NotFoundException;
+import org.sonar.server.user.MockUserSession;
import static com.google.common.collect.Lists.newArrayList;
import static org.fest.assertions.Assertions.assertThat;
import static org.fest.assertions.Fail.fail;
import static org.mockito.Matchers.any;
-import static org.mockito.Mockito.doAnswer;
-import static org.mockito.Mockito.when;
+import static org.mockito.Mockito.*;
@RunWith(MockitoJUnitRunner.class)
public class DebtModelServiceTest {
@Mock
SqlSession session;
- DebtModelService service;
-
CharacteristicDto rootCharacteristicDto = new CharacteristicDto()
.setId(1)
.setKey("MEMORY_EFFICIENCY")
.setName("Memory use")
- .setOrder(1);
+ .setOrder(2);
CharacteristicDto characteristicDto = new CharacteristicDto()
.setId(2)
int currentId;
+ DebtModelService service;
+
@Before
public void setUp() throws Exception {
- currentId = 10;
+ MockUserSession.set().setGlobalPermissions(GlobalPermissions.SYSTEM_ADMIN);
+ currentId = 10;
// Associate an id when inserting an object to simulate the db id generator
doAnswer(new Answer() {
public Object answer(InvocationOnMock invocation) {
assertThat(characteristic.id()).isEqualTo(1);
assertThat(characteristic.key()).isEqualTo("MEMORY_EFFICIENCY");
assertThat(characteristic.name()).isEqualTo("Memory use");
- assertThat(characteristic.order()).isEqualTo(1);
+ assertThat(characteristic.order()).isEqualTo(2);
assertThat(characteristic.parentId()).isNull();
assertThat(service.characteristicById(111)).isNull();
@Test
public void create_sub_characteristic() {
- when(dao.selectById(1)).thenReturn(rootCharacteristicDto);
+ when(dao.selectById(1, session)).thenReturn(rootCharacteristicDto);
- DebtCharacteristic result = service.createCharacteristic("Compilation name", 1);
+ DebtCharacteristic result = service.create("Compilation name", 1);
assertThat(result.id()).isEqualTo(currentId);
assertThat(result.key()).isEqualTo("COMPILATION_NAME");
@Test
public void fail_to_create_sub_characteristic_when_parent_id_is_not_a_root_characteristic() {
- when(dao.selectById(1)).thenReturn(characteristicDto);
+ when(dao.selectById(1, session)).thenReturn(characteristicDto);
try {
- service.createCharacteristic("Compilation", 1);
+ service.create("Compilation", 1);
fail();
} catch (Exception e) {
assertThat(e).isInstanceOf(BadRequestException.class).hasMessage("A sub characteristic can not have a sub characteristic as parent.");
@Test
public void fail_to_create_sub_characteristic_when_parent_does_not_exists() {
- when(dao.selectById(1)).thenReturn(null);
+ when(dao.selectById(1, session)).thenReturn(null);
try {
- service.createCharacteristic("Compilation", 1);
+ service.create("Compilation", 1);
fail();
} catch (Exception e) {
assertThat(e).isInstanceOf(NotFoundException.class).hasMessage("Characteristic with id 1 does not exists.");
@Test
public void fail_to_create_sub_characteristic_when_name_already_used() {
when(dao.selectByName("Compilation", session)).thenReturn(new CharacteristicDto());
- when(dao.selectById(1)).thenReturn(rootCharacteristicDto);
+ when(dao.selectById(1, session)).thenReturn(rootCharacteristicDto);
try {
- service.createCharacteristic("Compilation", 1);
+ service.create("Compilation", 1);
fail();
} catch (BadRequestException e) {
assertThat(e.l10nKey()).isEqualTo("errors.is_already_used");
}
}
+ @Test
+ public void fail_to_create_sub_characteristic_when_wrong_permission() {
+ MockUserSession.set().setGlobalPermissions(GlobalPermissions.DASHBOARD_SHARING);
+
+ try {
+ service.create("Compilation", 1);
+ fail();
+ } catch (Exception e) {
+ assertThat(e).isInstanceOf(ForbiddenException.class);
+ }
+ }
+
@Test
public void create_characteristic() {
- when(dao.selectMaxCharacteristicOrder(session)).thenReturn(1);
+ when(dao.selectMaxCharacteristicOrder(session)).thenReturn(2);
- DebtCharacteristic result = service.createCharacteristic("Portability", null);
+ DebtCharacteristic result = service.create("Portability", null);
assertThat(result.id()).isEqualTo(currentId);
assertThat(result.key()).isEqualTo("PORTABILITY");
assertThat(result.name()).isEqualTo("Portability");
- assertThat(result.order()).isEqualTo(2);
+ assertThat(result.order()).isEqualTo(3);
+ }
+
+ @Test
+ public void create_first_characteristic() {
+ when(dao.selectMaxCharacteristicOrder(session)).thenReturn(0);
+
+ DebtCharacteristic result = service.create("Portability", null);
+
+ assertThat(result.id()).isEqualTo(currentId);
+ assertThat(result.key()).isEqualTo("PORTABILITY");
+ assertThat(result.name()).isEqualTo("Portability");
+ assertThat(result.order()).isEqualTo(1);
+ }
+
+ @Test
+ public void rename_characteristic() {
+ when(dao.selectById(10, session)).thenReturn(characteristicDto);
+
+ DebtCharacteristic result = service.rename(10, "New Efficiency");
+
+ assertThat(result.key()).isEqualTo("EFFICIENCY");
+ assertThat(result.name()).isEqualTo("New Efficiency");
+ }
+
+ @Test
+ public void not_rename_characteristic_when_renaming_with_same_name() {
+ when(dao.selectById(10, session)).thenReturn(characteristicDto);
+
+ service.rename(10, "Efficiency");
+
+ verify(dao, never()).update(any(CharacteristicDto.class), eq(session));
+ }
+
+ @Test
+ public void fail_to_rename_unknown_characteristic() {
+ when(dao.selectById(10, session)).thenReturn(null);
+
+ try {
+ service.rename(10, "New Efficiency");
+ fail();
+ } catch (Exception e) {
+ assertThat(e).isInstanceOf(NotFoundException.class).hasMessage("Characteristic with id 10 does not exists.");
+ }
+ }
+
+ @Test
+ public void move_up() {
+ when(dao.selectById(10, session)).thenReturn(new CharacteristicDto().setId(10).setOrder(2));
+ when(dao.selectPrevious(2, session)).thenReturn(new CharacteristicDto().setId(2).setOrder(1));
+
+ DebtCharacteristic result = service.moveUp(10);
+
+ ArgumentCaptor<CharacteristicDto> argument = ArgumentCaptor.forClass(CharacteristicDto.class);
+ verify(dao, times(2)).update(argument.capture(), eq(session));
+
+ assertThat(result.order()).isEqualTo(1);
+ assertThat(argument.getAllValues().get(0).getOrder()).isEqualTo(2);
+ assertThat(argument.getAllValues().get(1).getOrder()).isEqualTo(1);
+ }
+
+ @Test
+ public void do_nothing_when_move_up_and_already_on_top() {
+ CharacteristicDto dto = new CharacteristicDto().setId(10).setOrder(1);
+ when(dao.selectById(10, session)).thenReturn(dto);
+ when(dao.selectPrevious(1, session)).thenReturn(null);
+
+ service.moveUp(10);
+
+ verify(dao, never()).update(any(CharacteristicDto.class), eq(session));
+ }
+
+ @Test
+ public void move_down() {
+ when(dao.selectById(10, session)).thenReturn(new CharacteristicDto().setId(10).setOrder(2));
+ when(dao.selectNext(2, session)).thenReturn(new CharacteristicDto().setId(2).setOrder(3));
+
+ DebtCharacteristic result = service.moveDown(10);
+
+ ArgumentCaptor<CharacteristicDto> argument = ArgumentCaptor.forClass(CharacteristicDto.class);
+ verify(dao, times(2)).update(argument.capture(), eq(session));
+
+ assertThat(result.order()).isEqualTo(3);
+ assertThat(argument.getAllValues().get(0).getOrder()).isEqualTo(2);
+ assertThat(argument.getAllValues().get(1).getOrder()).isEqualTo(3);
+ }
+
+ @Test
+ public void do_nothing_when_move_down_and_already_on_bottom() {
+ CharacteristicDto dto = new CharacteristicDto().setId(10).setOrder(5);
+ when(dao.selectById(10, session)).thenReturn(dto);
+ when(dao.selectNext(5, session)).thenReturn(null);
+
+ service.moveDown(10);
+
+ verify(dao, never()).update(any(CharacteristicDto.class), eq(session));
}
}