import org.sonar.api.server.debt.DebtCharacteristic;
import org.sonar.api.server.debt.DebtRemediationFunction;
import org.sonar.api.server.debt.internal.DefaultDebtCharacteristic;
+import org.sonar.api.server.rule.RulesDefinition;
import org.sonar.api.utils.System2;
import org.sonar.api.utils.ValidationMessages;
import org.sonar.core.permission.GlobalPermissions;
import org.sonar.core.rule.RuleDto;
import org.sonar.core.technicaldebt.db.CharacteristicDao;
import org.sonar.core.technicaldebt.db.CharacteristicDto;
+import org.sonar.server.rule.RuleDefinitionsLoader;
import org.sonar.server.rule.RuleRegistry;
import org.sonar.server.user.UserSession;
private final DebtRulesXMLImporter rulesXMLImporter;
private final DebtModelXMLExporter debtModelXMLExporter;
private final RuleRegistry ruleRegistry;
+ private final RuleDefinitionsLoader defLoader;
private final System2 system2;
public DebtModelBackup(MyBatis mybatis, CharacteristicDao dao, RuleDao ruleDao, DebtModelOperations debtModelOperations, DebtModelPluginRepository debtModelPluginRepository,
DebtCharacteristicsXMLImporter characteristicsXMLImporter, DebtRulesXMLImporter rulesXMLImporter,
- DebtModelXMLExporter debtModelXMLExporter, RuleRegistry ruleRegistry) {
- this(mybatis, dao, ruleDao, debtModelOperations, debtModelPluginRepository, characteristicsXMLImporter, rulesXMLImporter, debtModelXMLExporter, ruleRegistry,
+ DebtModelXMLExporter debtModelXMLExporter, RuleRegistry ruleRegistry, RuleDefinitionsLoader defLoader) {
+ this(mybatis, dao, ruleDao, debtModelOperations, debtModelPluginRepository, characteristicsXMLImporter, rulesXMLImporter, debtModelXMLExporter, ruleRegistry, defLoader,
System2.INSTANCE);
}
@VisibleForTesting
DebtModelBackup(MyBatis mybatis, CharacteristicDao dao, RuleDao ruleDao, DebtModelOperations debtModelOperations, DebtModelPluginRepository debtModelPluginRepository,
- DebtCharacteristicsXMLImporter characteristicsXMLImporter, DebtRulesXMLImporter rulesXMLImporter,DebtModelXMLExporter debtModelXMLExporter,
- RuleRegistry ruleRegistry, System2 system2) {
+ DebtCharacteristicsXMLImporter characteristicsXMLImporter, DebtRulesXMLImporter rulesXMLImporter, DebtModelXMLExporter debtModelXMLExporter,
+ RuleRegistry ruleRegistry, RuleDefinitionsLoader defLoader, System2 system2) {
this.mybatis = mybatis;
this.dao = dao;
this.ruleDao = ruleDao;
this.rulesXMLImporter = rulesXMLImporter;
this.debtModelXMLExporter = debtModelXMLExporter;
this.ruleRegistry = ruleRegistry;
+ this.defLoader = defLoader;
this.system2 = system2;
}
* Reset from provided model
*/
public void reset() {
- restoreProvidedModel();
- }
-
- private void restoreProvidedModel() {
checkPermission();
Date updateDate = new Date(system2.now());
SqlSession session = mybatis.openSession();
try {
- restoreCharacteristics(loadModelFromPlugin(DebtModelPluginRepository.DEFAULT_MODEL), true, updateDate, session);
+ // Restore characteristics
+ List<CharacteristicDto> allCharacteristicDtos = restoreCharacteristics(loadModelFromPlugin(DebtModelPluginRepository.DEFAULT_MODEL), true, updateDate, session);
+
+ // Load default rule definitions
+ RulesDefinition.Context context = defLoader.load();
+ List<RulesDefinition.Rule> rules = newArrayList();
+ for (RulesDefinition.Repository repoDef : context.repositories()) {
+ rules.addAll(repoDef.rules());
+ }
+
+ // Restore rules
List<RuleDto> ruleDtos = rules(null, session);
for (RuleDto rule : ruleDtos) {
+ // Restore default debt definitions
+ RulesDefinition.Rule ruleDef = ruleDef(rule, rules);
+ String subCharacteristicKey = ruleDef.debtSubCharacteristic();
+ CharacteristicDto subCharacteristicDto = characteristicByKey(subCharacteristicKey, allCharacteristicDtos);
+ DebtRemediationFunction remediationFunction = ruleDef.debtRemediationFunction();
+ boolean hasDebtDefinition = subCharacteristicDto != null && remediationFunction != null;
+ rule.setDefaultSubCharacteristicId(hasDebtDefinition ? subCharacteristicDto.getId() : null);
+ rule.setDefaultRemediationFunction(hasDebtDefinition ? remediationFunction.type().name() : null);
+ rule.setDefaultRemediationCoefficient(hasDebtDefinition ? remediationFunction.coefficient() : null);
+ rule.setDefaultRemediationOffset(hasDebtDefinition ? remediationFunction.offset() : null);
+
+ // Reset overridden debt definitions
rule.setSubCharacteristicId(null);
rule.setRemediationFunction(null);
rule.setRemediationCoefficient(null);
private void restoreRules(List<CharacteristicDto> allCharacteristicDtos, List<RuleDto> rules, List<RuleDebt> ruleDebts,
ValidationMessages validationMessages, Date updateDate, SqlSession session) {
for (RuleDto rule : rules) {
- RuleDebt ruleDebt = ruleDebtByRule(rule, ruleDebts);
+ RuleDebt ruleDebt = ruleDebt(rule, ruleDebts);
if (ruleDebt == null) {
// rule does not exists in the XML
disabledOverriddenRuleDebt(rule);
} else {
// Update only if modifications
if (ObjectUtils.notEqual(sourceCharacteristic.getName(), targetCharacteristic.name()) ||
- ObjectUtils.notEqual(sourceCharacteristic.getOrder(), targetCharacteristic.order())) {
+ ObjectUtils.notEqual(sourceCharacteristic.getOrder(), targetCharacteristic.order()) ||
+ ObjectUtils.notEqual(sourceCharacteristic.getParentId(), parentId)) {
sourceCharacteristic.setName(targetCharacteristic.name());
sourceCharacteristic.setOrder(targetCharacteristic.order());
+ sourceCharacteristic.setParentId(parentId);
sourceCharacteristic.setUpdatedAt(updateDate);
dao.update(sourceCharacteristic, session);
}
}
@CheckForNull
- private static RuleDebt ruleDebtByRule(final RuleDto rule, List<RuleDebt> ruleDebts) {
+ private static RuleDebt ruleDebt(final RuleDto rule, List<RuleDebt> ruleDebts) {
if (ruleDebts.isEmpty()) {
return null;
}
}, null);
}
+ private static RulesDefinition.Rule ruleDef(final RuleDto rule, List<RulesDefinition.Rule> rules) {
+ return Iterables.find(rules, new Predicate<RulesDefinition.Rule>() {
+ @Override
+ public boolean apply(@Nullable RulesDefinition.Rule input) {
+ return input != null && rule.getRepositoryKey().equals(input.repository().key()) && rule.getRuleKey().equals(input.key());
+ }
+ });
+ }
+
@CheckForNull
- private static CharacteristicDto characteristicByKey(final String key, List<CharacteristicDto> characteristicDtos) {
+ private static CharacteristicDto characteristicByKey(@Nullable final String key, List<CharacteristicDto> characteristicDtos) {
+ if (key == null) {
+ return null;
+ }
return Iterables.find(characteristicDtos, new Predicate<CharacteristicDto>() {
@Override
public boolean apply(@Nullable CharacteristicDto input) {
@CheckForNull
private CharacteristicDto findCharacteristicToSwitchWith(final CharacteristicDto dto, final boolean moveUpOrDown, SqlSession session) {
- // characteristics should be order by 'order'
+ // characteristics should be sort by 'order'
List<CharacteristicDto> rootCharacteristics = dao.selectEnabledRootCharacteristics(session);
int currentPosition = Iterables.indexOf(rootCharacteristics, new Predicate<CharacteristicDto>() {
@Override
public void disableRulesDebt(List<RuleDto> ruleDtos, Integer subCharacteristicId, Date updateDate, SqlSession session) {
for (RuleDto ruleDto : ruleDtos) {
if (subCharacteristicId.equals(ruleDto.getSubCharacteristicId())) {
- ruleDto.setSubCharacteristicId(null);
+ ruleDto.setSubCharacteristicId(RuleDto.DISABLED_CHARACTERISTIC_ID);
ruleDto.setRemediationFunction(null);
ruleDto.setRemediationCoefficient(null);
ruleDto.setRemediationOffset(null);
pico.addSingleton(RuleRegistry.class);
pico.addSingleton(RubyRuleService.class);
pico.addSingleton(RuleRepositories.class);
+ pico.addSingleton(DeprecatedRulesDefinition.class);
+ pico.addSingleton(RuleDefinitionsLoader.class);
pico.addSingleton(RulesWs.class);
pico.addSingleton(RuleShowWsHandler.class);
pico.addSingleton(RuleSearchWsHandler.class);
startupContainer.addSingleton(GwtPublisher.class);
startupContainer.addSingleton(RegisterMetrics.class);
startupContainer.addSingleton(RegisterQualityGates.class);
- startupContainer.addSingleton(DeprecatedRulesDefinition.class);
- startupContainer.addSingleton(RuleDefinitionsLoader.class);
startupContainer.addSingleton(RegisterRules.class);
startupContainer.addSingleton(RegisterQualityProfiles.class);
startupContainer.addSingleton(JdbcDriverDeployer.class);
dto.setLanguage(def.repository().language());
changed = true;
}
- CharacteristicDto characteristic = buffer.characteristic(def.debtSubCharacteristic(), def.repository().key(), def.key(), dto.getSubCharacteristicId());
- changed = mergeDebtDefinitions(def, dto, characteristic) || changed;
+ CharacteristicDto subCharacteristic = buffer.characteristic(def.debtSubCharacteristic(), def.repository().key(), def.key(), dto.getSubCharacteristicId());
+ changed = mergeDebtDefinitions(def, dto, subCharacteristic) || changed;
if (changed) {
dto.setUpdatedAt(buffer.now());
}
return changed;
}
- private boolean mergeDebtDefinitions(RulesDefinition.Rule def, RuleDto dto, @Nullable CharacteristicDto characteristic) {
+ private boolean mergeDebtDefinitions(RulesDefinition.Rule def, RuleDto dto, @Nullable CharacteristicDto subCharacteristic) {
boolean changed = false;
- // Debt definitions are set to null if the characteristic is null or unknown
- boolean hasCharacteristic = characteristic != null;
+ // Debt definitions are set to null if the sub-characteristic is null or unknown
+ boolean hasCharacteristic = subCharacteristic != null;
DebtRemediationFunction debtRemediationFunction = hasCharacteristic ? def.debtRemediationFunction() : null;
- Integer characteristicId = hasCharacteristic ? characteristic.getId() : null;
+ Integer characteristicId = hasCharacteristic ? subCharacteristic.getId() : null;
String remediationFactor = hasCharacteristic ? debtRemediationFunction.coefficient() : null;
String remediationOffset = hasCharacteristic ? debtRemediationFunction.offset() : null;
String effortToFixDescription = hasCharacteristic ? def.effortToFixDescription() : null;
import org.mockito.runners.MockitoJUnitRunner;
import org.mockito.stubbing.Answer;
import org.sonar.api.rule.RuleKey;
+import org.sonar.api.rule.RuleStatus;
+import org.sonar.api.rule.Severity;
import org.sonar.api.server.debt.DebtRemediationFunction;
import org.sonar.api.server.debt.internal.DefaultDebtCharacteristic;
+import org.sonar.api.server.rule.RulesDefinition;
import org.sonar.api.utils.DateUtils;
import org.sonar.api.utils.System2;
import org.sonar.api.utils.ValidationMessages;
import org.sonar.core.rule.RuleDto;
import org.sonar.core.technicaldebt.db.CharacteristicDao;
import org.sonar.core.technicaldebt.db.CharacteristicDto;
+import org.sonar.server.rule.RuleDefinitionsLoader;
import org.sonar.server.rule.RuleRegistry;
import org.sonar.server.user.MockUserSession;
@Mock
RuleRegistry ruleRegistry;
+ @Mock
+ RuleDefinitionsLoader defLoader;
+
@Mock
System2 system2;
when(debtModelPluginRepository.createReaderForXMLFile("technical-debt")).thenReturn(defaultModelReader);
debtModelBackup = new DebtModelBackup(myBatis, dao, ruleDao, debtModelOperations, debtModelPluginRepository, characteristicsXMLImporter, rulesXMLImporter,
- debtModelXMLExporter, ruleRegistry, system2);
+ debtModelXMLExporter, ruleRegistry, defLoader, system2);
}
@Test
@Test
public void update_characteristics_when_restoring_characteristics() throws Exception {
when(dao.selectEnabledCharacteristics(session)).thenReturn(newArrayList(
+ // Order and name have changed
new CharacteristicDto().setId(1).setKey("PORTABILITY").setName("Portability updated").setOrder(2).setCreatedAt(oldDate).setUpdatedAt(oldDate),
new CharacteristicDto().setId(2).setKey("COMPILER").setName("Compiler updated").setParentId(1).setCreatedAt(oldDate).setUpdatedAt(oldDate)
));
assertThat(dto2.getUpdatedAt()).isEqualTo(now);
}
+ @Test
+ public void update_parent_when_restoring_characteristics() throws Exception {
+ when(dao.selectEnabledCharacteristics(session)).thenReturn(newArrayList(
+ // Parent has changed
+ new CharacteristicDto().setId(1).setKey("PORTABILITY").setName("Portability updated").setParentId(1).setOrder(1).setCreatedAt(oldDate).setUpdatedAt(oldDate),
+ new CharacteristicDto().setId(2).setKey("COMPILER").setName("Compiler updated").setCreatedAt(oldDate).setUpdatedAt(oldDate)
+ ));
+
+ debtModelBackup.restoreCharacteristics(
+ new DebtModel()
+ .addRootCharacteristic(new DefaultDebtCharacteristic().setKey("PORTABILITY").setName("Portability").setOrder(1))
+ .addSubCharacteristic(new DefaultDebtCharacteristic().setKey("COMPILER").setName("Compiler"), "PORTABILITY"),
+ true,
+ now,
+ session
+ );
+
+ verify(dao, times(2)).update(characteristicCaptor.capture(), eq(session));
+
+ CharacteristicDto dto1 = characteristicCaptor.getAllValues().get(0);
+ assertThat(dto1.getId()).isEqualTo(1);
+ assertThat(dto1.getKey()).isEqualTo("PORTABILITY");
+ assertThat(dto1.getParentId()).isNull();
+ assertThat(dto1.getUpdatedAt()).isEqualTo(now);
+
+ CharacteristicDto dto2 = characteristicCaptor.getAllValues().get(1);
+ assertThat(dto2.getId()).isEqualTo(2);
+ assertThat(dto2.getKey()).isEqualTo("COMPILER");
+ assertThat(dto2.getParentId()).isEqualTo(1);
+ assertThat(dto2.getUpdatedAt()).isEqualTo(now);
+ }
+
@Test
public void disable_no_more_existing_characteristics_when_restoring_characteristics() throws Exception {
CharacteristicDto dto1 = new CharacteristicDto().setId(1).setKey("PORTABILITY").setName("Portability").setOrder(1);
}
@Test
- public void reset_from_provided_model() throws Exception {
+ public void reset_model() throws Exception {
when(characteristicsXMLImporter.importXML(any(Reader.class))).thenReturn(new DebtModel()
.addRootCharacteristic(new DefaultDebtCharacteristic().setKey("PORTABILITY").setName("Portability").setOrder(1))
.addSubCharacteristic(new DefaultDebtCharacteristic().setKey("COMPILER").setName("Compiler"), "PORTABILITY"));
));
when(ruleDao.selectEnablesAndNonManual(session)).thenReturn(newArrayList(
- new RuleDto().setRepositoryKey("squid").setSubCharacteristicId(2).setRemediationFunction("LINEAR_OFFSET").setRemediationCoefficient("2h").setRemediationOffset("15min")
+ new RuleDto().setRepositoryKey("squid").setRuleKey("NPE")
+ .setDefaultSubCharacteristicId(10).setDefaultRemediationFunction("LINEAR").setDefaultRemediationCoefficient("2h")
+ .setSubCharacteristicId(2).setRemediationFunction("LINEAR_OFFSET").setRemediationCoefficient("2h").setRemediationOffset("15min")
.setCreatedAt(oldDate).setUpdatedAt(oldDate)
));
+ RulesDefinition.Context context = new RulesDefinition.Context();
+ RulesDefinition.NewRepository repo = context.createRepository("squid", "java").setName("Squid");
+ RulesDefinition.NewRule newRule = repo.createRule("NPE")
+ .setName("Detect NPE")
+ .setHtmlDescription("Detect <code>java.lang.NullPointerException</code>")
+ .setSeverity(Severity.BLOCKER)
+ .setStatus(RuleStatus.BETA)
+ .setDebtSubCharacteristic("COMPILER");
+ newRule.setDebtRemediationFunction(newRule.debtRemediationFunctions().linearWithOffset("4h", "20min"));
+ repo.done();
+ when(defLoader.load()).thenReturn(context);
+
debtModelBackup.reset();
verify(dao).selectEnabledCharacteristics(session);
verify(session).commit();
RuleDto rule = ruleCaptor.getValue();
+
+ assertThat(rule.getDefaultSubCharacteristicId()).isEqualTo(2);
+ assertThat(rule.getDefaultRemediationFunction()).isEqualTo("LINEAR_OFFSET");
+ assertThat(rule.getDefaultRemediationCoefficient()).isEqualTo("4h");
+ assertThat(rule.getDefaultRemediationOffset()).isEqualTo("20min");
+ assertThat(rule.getUpdatedAt()).isEqualTo(now);
+
assertThat(rule.getSubCharacteristicId()).isNull();
assertThat(rule.getRemediationFunction()).isNull();
assertThat(rule.getRemediationCoefficient()).isNull();
assertThat(rule.getUpdatedAt()).isEqualTo(now);
}
+ @Test
+ public void reset_model_when_no_default_value() throws Exception {
+ when(characteristicsXMLImporter.importXML(any(Reader.class))).thenReturn(new DebtModel()
+ .addRootCharacteristic(new DefaultDebtCharacteristic().setKey("PORTABILITY").setName("Portability").setOrder(1))
+ .addSubCharacteristic(new DefaultDebtCharacteristic().setKey("COMPILER").setName("Compiler"), "PORTABILITY"));
+
+ when(dao.selectEnabledCharacteristics(session)).thenReturn(newArrayList(
+ new CharacteristicDto().setId(1).setKey("PORTABILITY").setName("Portability updated").setOrder(2).setCreatedAt(oldDate),
+ new CharacteristicDto().setId(2).setKey("COMPILER").setName("Compiler updated").setParentId(1).setCreatedAt(oldDate)
+ ));
+
+ when(ruleDao.selectEnablesAndNonManual(session)).thenReturn(newArrayList(
+ new RuleDto().setRepositoryKey("squid").setRuleKey("NPE")
+ .setDefaultSubCharacteristicId(10).setDefaultRemediationFunction("LINEAR").setDefaultRemediationCoefficient("2h")
+ .setSubCharacteristicId(2).setRemediationFunction("LINEAR_OFFSET").setRemediationCoefficient("2h").setRemediationOffset("15min")
+ .setCreatedAt(oldDate).setUpdatedAt(oldDate)
+ ));
+
+ RulesDefinition.Context context = new RulesDefinition.Context();
+ RulesDefinition.NewRepository repo = context.createRepository("squid", "java").setName("Squid");
+ repo.createRule("NPE")
+ .setName("Detect NPE")
+ .setHtmlDescription("Detect <code>java.lang.NullPointerException</code>")
+ .setSeverity(Severity.BLOCKER)
+ .setStatus(RuleStatus.BETA);
+ repo.done();
+ when(defLoader.load()).thenReturn(context);
+
+ debtModelBackup.reset();
+
+ verify(dao).selectEnabledCharacteristics(session);
+ verify(dao, times(2)).update(any(CharacteristicDto.class), eq(session));
+ verifyNoMoreInteractions(dao);
+
+ verify(ruleDao).selectEnablesAndNonManual(session);
+ verify(ruleDao).update(ruleCaptor.capture(), eq(session));
+ verifyNoMoreInteractions(ruleDao);
+ verify(ruleRegistry).reindex(ruleCaptor.getAllValues(), session);
+
+ verify(session).commit();
+
+ RuleDto rule = ruleCaptor.getValue();
+ assertThat(rule.getDefaultSubCharacteristicId()).isNull();
+ assertThat(rule.getDefaultRemediationFunction()).isNull();
+ assertThat(rule.getDefaultRemediationCoefficient()).isNull();
+ assertThat(rule.getDefaultRemediationOffset()).isNull();
+ assertThat(rule.getUpdatedAt()).isEqualTo(now);
+ }
+
@Test
public void restore_from_xml_with_different_characteristic_and_same_function() throws Exception {
when(characteristicsXMLImporter.importXML(anyString())).thenReturn(new DebtModel()
assertThat(ruleDto.getUpdatedAt()).isEqualTo(now);
// Overridden debt data are disabled
- assertThat(ruleDto.getSubCharacteristicId()).isNull();
+ assertThat(ruleDto.getSubCharacteristicId()).isEqualTo(-1);
assertThat(ruleDto.getRemediationFunction()).isNull();
assertThat(ruleDto.getRemediationCoefficient()).isNull();
assertThat(ruleDto.getRemediationOffset()).isNull();