@@ -37,6 +37,7 @@ public class ComponentDto extends Dto<String> implements Component { | |||
private String language; | |||
private Long projectId; | |||
private Long subProjectId; | |||
private boolean enabled = true; | |||
public Long getId() { | |||
return id; | |||
@@ -136,6 +137,15 @@ public class ComponentDto extends Dto<String> implements Component { | |||
return this; | |||
} | |||
public boolean isEnabled() { | |||
return enabled; | |||
} | |||
public ComponentDto setEnabled(boolean enabled) { | |||
this.enabled = enabled; | |||
return this; | |||
} | |||
@Override | |||
public String getKey() { | |||
return kee; |
@@ -31,4 +31,6 @@ public interface ComponentMapper { | |||
ComponentDto selectById(long id); | |||
long countById(long id); | |||
void insert(ComponentDto rule); | |||
} |
@@ -35,5 +35,14 @@ | |||
WHERE p.enabled=${_true} AND p.id=#{id} | |||
</select> | |||
<sql id="insertColumns"> | |||
(kee, name, long_name, qualifier, scope, language, root_id, path, created_at) | |||
</sql> | |||
<insert id="insert" parameterType="Component" keyColumn="id" useGeneratedKeys="true" keyProperty="id"> | |||
insert into projects <include refid="insertColumns"/> | |||
values (#{kee}, #{name}, #{longName}, #{qualifier}, #{scope}, #{language}, #{subProjectId}, #{path}, #{createdAt}) | |||
</insert> | |||
</mapper> | |||
@@ -62,7 +62,8 @@ public class ComponentDao extends BaseDao<ComponentMapper, ComponentDto, String> | |||
@Override | |||
protected ComponentDto doInsert(DbSession session, ComponentDto item) { | |||
throw notImplemented(); | |||
getMapper(session).insert(item); | |||
return item; | |||
} | |||
@Override | |||
@@ -75,12 +76,12 @@ public class ComponentDao extends BaseDao<ComponentMapper, ComponentDto, String> | |||
throw notImplemented(); | |||
} | |||
private static IllegalStateException notImplemented() { | |||
throw new IllegalStateException("Not implemented yet"); | |||
} | |||
@Override | |||
public void synchronizeAfter(DbSession session, long timestamp) { | |||
throw notImplemented(); | |||
} | |||
private static IllegalStateException notImplemented() { | |||
throw new IllegalStateException("Not implemented yet"); | |||
} | |||
} |
@@ -51,6 +51,12 @@ import org.sonar.server.db.DbClient; | |||
import org.sonar.server.exceptions.NotFoundException; | |||
import org.sonar.server.issue.IssueService; | |||
import org.sonar.server.issue.RulesAggregation; | |||
import org.sonar.server.rule.Rule; | |||
import org.sonar.server.rule.RuleService; | |||
import org.sonar.server.rule.index.RuleDoc; | |||
import org.sonar.server.rule.index.RuleQuery; | |||
import org.sonar.server.search.QueryOptions; | |||
import org.sonar.server.search.Result; | |||
import org.sonar.server.ui.ViewProxy; | |||
import org.sonar.server.ui.Views; | |||
import org.sonar.server.user.UserSession; | |||
@@ -74,14 +80,16 @@ public class ComponentAppAction implements RequestHandler { | |||
private final IssueService issueService; | |||
private final Views views; | |||
private final RuleService ruleService; | |||
private final Periods periods; | |||
private final Durations durations; | |||
private final I18n i18n; | |||
public ComponentAppAction(DbClient dbClient, IssueService issueService, Views views, Periods periods, Durations durations, I18n i18n) { | |||
public ComponentAppAction(DbClient dbClient, IssueService issueService, Views views, RuleService ruleService, Periods periods, Durations durations, I18n i18n) { | |||
this.dbClient = dbClient; | |||
this.issueService = issueService; | |||
this.views = views; | |||
this.ruleService = ruleService; | |||
this.periods = periods; | |||
this.durations = durations; | |||
this.i18n = i18n; | |||
@@ -93,7 +101,7 @@ public class ComponentAppAction implements RequestHandler { | |||
.setSince("4.4") | |||
.setInternal(true) | |||
.setHandler(this) | |||
.setResponseExample(Resources.getResource(this.getClass(), "components-app-example-show.json")); | |||
.setResponseExample(Resources.getResource(this.getClass(), "components-example-app.json")); | |||
action | |||
.createParam(PARAM_KEY) | |||
@@ -119,7 +127,7 @@ public class ComponentAppAction implements RequestHandler { | |||
try { | |||
ComponentDto component = dbClient.componentDao().getNullableByKey(session, fileKey); | |||
if (component == null) { | |||
throw new NotFoundException(String.format("Component '%s' does not exists.", fileKey)); | |||
throw new NotFoundException(String.format("Component '%s' does not exist", fileKey)); | |||
} | |||
userSession.checkComponentPermission(UserRole.USER, fileKey); | |||
@@ -138,6 +146,7 @@ public class ComponentAppAction implements RequestHandler { | |||
appendMeasures(json, measuresByMetricKey, severitiesAggregation, periodIndex); | |||
appendTabs(json, measuresByMetricKey); | |||
appendExtensions(json, component, userSession); | |||
appendManualRules(json); | |||
} finally { | |||
MyBatis.closeQuietly(session); | |||
} | |||
@@ -148,12 +157,12 @@ public class ComponentAppAction implements RequestHandler { | |||
private void appendComponent(JsonWriter json, ComponentDto component, UserSession userSession, DbSession session) { | |||
List<PropertyDto> propertyDtos = dbClient.propertiesDao().selectByQuery(PropertyQuery.builder() | |||
.setKey("favourite") | |||
.setComponentId(component.getId()) | |||
.setUserId(userSession.userId()) | |||
.build(), | |||
.setKey("favourite") | |||
.setComponentId(component.getId()) | |||
.setUserId(userSession.userId()) | |||
.build(), | |||
session | |||
); | |||
); | |||
boolean isFavourite = propertyDtos.size() == 1; | |||
json.prop("key", component.key()); | |||
@@ -190,8 +199,10 @@ public class ComponentAppAction implements RequestHandler { | |||
} | |||
private void appendPermissions(JsonWriter json, ComponentDto component, UserSession userSession) { | |||
json.prop("canMarkAsFavourite", userSession.isLoggedIn() && userSession.hasComponentPermission(UserRole.USER, component.key())); | |||
boolean hasBrowsePermission = userSession.hasComponentPermission(UserRole.USER, component.key()); | |||
json.prop("canMarkAsFavourite", userSession.isLoggedIn() && hasBrowsePermission); | |||
json.prop("canBulkChange", userSession.isLoggedIn()); | |||
json.prop("canCreateManualIssue", userSession.isLoggedIn() && hasBrowsePermission); | |||
} | |||
private void appendMeasures(JsonWriter json, Map<String, MeasureDto> measuresByMetricKey, Multiset<String> severitiesAggregation, Integer periodIndex) { | |||
@@ -245,6 +256,21 @@ public class ComponentAppAction implements RequestHandler { | |||
json.endArray(); | |||
} | |||
private void appendManualRules(JsonWriter json) { | |||
Result<Rule> result = ruleService.search(new RuleQuery().setRepositories(newArrayList(RuleDoc.MANUAL_REPOSITORY)), new QueryOptions().setMaxLimit()); | |||
if (result != null && !result.getHits().isEmpty()) { | |||
List<Rule> manualRules = result.getHits(); | |||
json.name("manual_rules").beginArray(); | |||
for (Rule manualRule : manualRules) { | |||
json.beginObject() | |||
.prop("key", manualRule.key().toString()) | |||
.prop("name", manualRule.name()) | |||
.endObject(); | |||
} | |||
json.endArray(); | |||
} | |||
} | |||
private void appendExtensions(JsonWriter json, ComponentDto component, UserSession userSession) { | |||
List<ViewProxy<Page>> extensionPages = views.getPages(NavigationSection.RESOURCE_TAB, component.scope(), component.qualifier(), component.language(), null); | |||
Map<String, String> extensions = extensions(extensionPages, component, userSession); | |||
@@ -311,7 +337,6 @@ public class ComponentAppAction implements RequestHandler { | |||
return measuresByMetricKey; | |||
} | |||
@CheckForNull | |||
private Date periodDate(@Nullable final Integer periodIndex, List<Period> periodList) { | |||
if (periodIndex != null) { |
@@ -8,6 +8,7 @@ | |||
"fav": true, | |||
"canMarkAsFavourite": true, | |||
"canBulkChange": false, | |||
"canCreateManualIssue": false, | |||
"periods": [ | |||
[ | |||
1, | |||
@@ -50,5 +51,8 @@ | |||
], | |||
"tabs": [ | |||
"scn", "coverage", "duplications" | |||
], | |||
"manual_rules": [ | |||
{"key": "manual:API", "name": "API"} | |||
] | |||
} |
@@ -22,11 +22,15 @@ package org.sonar.server.component.persistence; | |||
import org.junit.After; | |||
import org.junit.Before; | |||
import org.junit.Test; | |||
import org.sonar.api.utils.DateUtils; | |||
import org.sonar.api.utils.System2; | |||
import org.sonar.core.component.ComponentDto; | |||
import org.sonar.core.persistence.AbstractDaoTestCase; | |||
import org.sonar.core.persistence.DbSession; | |||
import static org.fest.assertions.Assertions.assertThat; | |||
import static org.mockito.Mockito.mock; | |||
import static org.mockito.Mockito.when; | |||
public class ComponentDaoTest extends AbstractDaoTestCase { | |||
@@ -34,10 +38,13 @@ public class ComponentDaoTest extends AbstractDaoTestCase { | |||
ComponentDao dao; | |||
System2 system2; | |||
@Before | |||
public void createDao() throws Exception { | |||
session = getMyBatis().openSession(false); | |||
dao = new ComponentDao(); | |||
system2 = mock(System2.class); | |||
dao = new ComponentDao(system2); | |||
} | |||
@After | |||
@@ -96,4 +103,49 @@ public class ComponentDaoTest extends AbstractDaoTestCase { | |||
assertThat(dao.existsById(4L, session)).isTrue(); | |||
assertThat(dao.existsById(111L, session)).isFalse(); | |||
} | |||
@Test | |||
public void insert() { | |||
when(system2.now()).thenReturn(DateUtils.parseDate("2014-06-18").getTime()); | |||
setupData("empty"); | |||
ComponentDto componentDto = new ComponentDto() | |||
.setId(1L) | |||
.setKey("org.struts:struts-core:src/org/struts/RequestContext.java") | |||
.setName("RequestContext.java") | |||
.setLongName("org.struts.RequestContext") | |||
.setQualifier("FIL") | |||
.setScope("FIL") | |||
.setLanguage("java") | |||
.setPath("src/org/struts/RequestContext.java") | |||
.setSubProjectId(3L) | |||
.setEnabled(true); | |||
dao.insert(session, componentDto); | |||
session.commit(); | |||
assertThat(componentDto.getId()).isNotNull(); | |||
checkTables("insert", "projects"); | |||
} | |||
@Test(expected = IllegalStateException.class) | |||
public void update() { | |||
dao.update(session, new ComponentDto() | |||
.setId(1L) | |||
.setKey("org.struts:struts-core:src/org/struts/RequestContext.java") | |||
); | |||
} | |||
@Test(expected = IllegalStateException.class) | |||
public void delete() { | |||
dao.delete(session, new ComponentDto() | |||
.setId(1L) | |||
.setKey("org.struts:struts-core:src/org/struts/RequestContext.java") | |||
); | |||
} | |||
@Test(expected = IllegalStateException.class) | |||
public void synchronize_after() { | |||
dao.synchronizeAfter(session, 1L); | |||
} | |||
} |
@@ -32,6 +32,7 @@ import org.mockito.Mock; | |||
import org.mockito.runners.MockitoJUnitRunner; | |||
import org.sonar.api.i18n.I18n; | |||
import org.sonar.api.measures.CoreMetrics; | |||
import org.sonar.api.rule.RuleKey; | |||
import org.sonar.api.utils.DateUtils; | |||
import org.sonar.api.utils.Duration; | |||
import org.sonar.api.utils.Durations; | |||
@@ -54,6 +55,11 @@ import org.sonar.server.db.DbClient; | |||
import org.sonar.server.issue.IssueService; | |||
import org.sonar.server.issue.RulesAggregation; | |||
import org.sonar.server.measure.persistence.MeasureDao; | |||
import org.sonar.server.rule.Rule; | |||
import org.sonar.server.rule.RuleService; | |||
import org.sonar.server.rule.index.RuleQuery; | |||
import org.sonar.server.search.QueryOptions; | |||
import org.sonar.server.search.Result; | |||
import org.sonar.server.ui.ViewProxy; | |||
import org.sonar.server.ui.Views; | |||
import org.sonar.server.user.MockUserSession; | |||
@@ -99,6 +105,9 @@ public class ComponentAppActionTest { | |||
@Mock | |||
Views views; | |||
@Mock | |||
RuleService ruleService; | |||
@Mock | |||
Periods periods; | |||
@@ -128,7 +137,7 @@ public class ComponentAppActionTest { | |||
when(issueService.findRulesByComponent(anyString(), any(Date.class), eq(session))).thenReturn(mock(RulesAggregation.class)); | |||
when(measureDao.findByComponentKeyAndMetricKeys(anyString(), anyListOf(String.class), eq(session))).thenReturn(measures); | |||
tester = new WsTester(new ComponentsWs(new ComponentAppAction(dbClient, issueService, views, periods, durations, i18n))); | |||
tester = new WsTester(new ComponentsWs(new ComponentAppAction(dbClient, issueService, views, ruleService, periods, durations, i18n))); | |||
} | |||
@Test | |||
@@ -409,6 +418,22 @@ public class ComponentAppActionTest { | |||
request.execute().assertJson(getClass(), "app_with_extension_having_permission.json"); | |||
} | |||
@Test | |||
public void app_with_manual_rules() throws Exception { | |||
MockUserSession.set().addComponentPermission(UserRole.USER, PROJECT_KEY, COMPONENT_KEY); | |||
addComponent(); | |||
Result<Rule> result = mock(Result.class); | |||
Rule rule = mock(Rule.class); | |||
when(rule.key()).thenReturn(RuleKey.of("manual", "API")); | |||
when(rule.name()).thenReturn("API"); | |||
when(result.getHits()).thenReturn(newArrayList(rule)); | |||
when(ruleService.search(any(RuleQuery.class), any(QueryOptions.class))).thenReturn(result); | |||
WsTester.TestRequest request = tester.newGetRequest("api/components", "app").setParam("key", COMPONENT_KEY); | |||
request.execute().assertJson(getClass(), "app_with_manual_rules.json"); | |||
} | |||
private void addComponent() { | |||
ComponentDto file = new ComponentDto().setId(10L).setQualifier("FIL").setKey(COMPONENT_KEY).setName("Plugin.java") | |||
.setPath("src/main/java/org/sonar/api/Plugin.java").setSubProjectId(5L).setProjectId(1L); |
@@ -29,6 +29,7 @@ import org.sonar.api.utils.Durations; | |||
import org.sonar.core.timemachine.Periods; | |||
import org.sonar.server.db.DbClient; | |||
import org.sonar.server.issue.IssueService; | |||
import org.sonar.server.rule.RuleService; | |||
import org.sonar.server.ui.Views; | |||
import org.sonar.server.ws.WsTester; | |||
@@ -42,7 +43,7 @@ public class ComponentsWsTest { | |||
@Before | |||
public void setUp() throws Exception { | |||
WsTester tester = new WsTester(new ComponentsWs(new ComponentAppAction(mock(DbClient.class), mock(IssueService.class), mock(Views.class), | |||
mock(Periods.class), mock(Durations.class), mock(I18n.class)))); | |||
mock(RuleService.class), mock(Periods.class), mock(Durations.class), mock(I18n.class)))); | |||
controller = tester.controller("api/components"); | |||
} | |||
@@ -0,0 +1,3 @@ | |||
<dataset> | |||
</dataset> |
@@ -0,0 +1,9 @@ | |||
<dataset> | |||
<projects id="1" kee="org.struts:struts-core:src/org/struts/RequestContext.java" name="RequestContext.java" long_name="org.struts.RequestContext" | |||
qualifier="FIL" scope="FIL" language="java" path="src/org/struts/RequestContext.java" root_id="3" | |||
description="[null]" enabled="[true]" copy_resource_id="[null]" person_id="[null]" deprecated_kee="[null]" | |||
created_at="2014-06-18" | |||
/> | |||
</dataset> |
@@ -8,6 +8,7 @@ | |||
"fav": true, | |||
"canMarkAsFavourite": true, | |||
"canBulkChange": true, | |||
"canCreateManualIssue": true, | |||
"periods": [], | |||
"severities": [], | |||
"rules": [], |
@@ -8,6 +8,7 @@ | |||
"fav": false, | |||
"canMarkAsFavourite": false, | |||
"canBulkChange": false, | |||
"canCreateManualIssue": false, | |||
"periods": [], | |||
"severities": [], | |||
"rules": [], |
@@ -8,6 +8,7 @@ | |||
"fav": false, | |||
"canMarkAsFavourite": false, | |||
"canBulkChange": false, | |||
"canCreateManualIssue": false, | |||
"periods": [], | |||
"severities": [], | |||
"rules": [], |
@@ -8,6 +8,7 @@ | |||
"fav": false, | |||
"canMarkAsFavourite": false, | |||
"canBulkChange": false, | |||
"canCreateManualIssue": false, | |||
"periods": [], | |||
"severities": [ | |||
["BLOCKER", "Blocker", 1], |
@@ -8,6 +8,7 @@ | |||
"fav": false, | |||
"canMarkAsFavourite": false, | |||
"canBulkChange": false, | |||
"canCreateManualIssue": false, | |||
"periods": [ | |||
[1, "since previous analysis (May 08 2014)", "2014-05-08T00:00:00+0200"] | |||
], |
@@ -0,0 +1,19 @@ | |||
{ | |||
"key": "org.codehaus.sonar:sonar-plugin-api:src/main/java/org/sonar/api/Plugin.java", | |||
"path": "src/main/java/org/sonar/api/Plugin.java", | |||
"name": "Plugin.java", | |||
"q": "FIL", | |||
"subProjectName": "SonarQube :: Plugin API", | |||
"projectName": "SonarQube", | |||
"fav": false, | |||
"canMarkAsFavourite": false, | |||
"canBulkChange": false, | |||
"canCreateManualIssue": false, | |||
"periods": [], | |||
"severities": [], | |||
"rules": [], | |||
"measures": {}, | |||
"manual_rules": [ | |||
{"key": "manual:API", "name": "API"} | |||
] | |||
} |
@@ -8,6 +8,7 @@ | |||
"fav": false, | |||
"canMarkAsFavourite": false, | |||
"canBulkChange": false, | |||
"canCreateManualIssue": false, | |||
"periods": [], | |||
"severities": [], | |||
"rules": [], |
@@ -8,6 +8,7 @@ | |||
"fav": false, | |||
"canMarkAsFavourite": false, | |||
"canBulkChange": false, | |||
"canCreateManualIssue": false, | |||
"periods": [ | |||
[1, "since previous analysis (May 08 2014)", "2014-05-08T00:00:00+0200"] | |||
], |
@@ -8,6 +8,7 @@ | |||
"fav": false, | |||
"canMarkAsFavourite": false, | |||
"canBulkChange": false, | |||
"canCreateManualIssue": false, | |||
"periods": [ | |||
[1, "since previous analysis (May 08 2014)", "2014-05-08T00:00:00+0200"] | |||
], |
@@ -8,6 +8,7 @@ | |||
"fav": false, | |||
"canMarkAsFavourite": false, | |||
"canBulkChange": false, | |||
"canCreateManualIssue": false, | |||
"periods": [], | |||
"severities": [], | |||
"rules": [ |
@@ -8,6 +8,7 @@ | |||
"fav": false, | |||
"canMarkAsFavourite": false, | |||
"canBulkChange": false, | |||
"canCreateManualIssue": false, | |||
"periods": [ | |||
[1, "since previous analysis (May 08 2014)", "2014-05-08T00:00:00+0200"] | |||
], |
@@ -8,6 +8,7 @@ | |||
"fav": false, | |||
"canMarkAsFavourite": false, | |||
"canBulkChange": false, | |||
"canCreateManualIssue": false, | |||
"periods": [], | |||
"severities": [ | |||
["MAJOR", "Major", 5] |
@@ -8,6 +8,7 @@ | |||
"fav": false, | |||
"canMarkAsFavourite": false, | |||
"canBulkChange": false, | |||
"canCreateManualIssue": false, | |||
"periods": [ | |||
[1, "since previous analysis (May 08 2014)", "2014-05-08T00:00:00+0200"] | |||
], |
@@ -7,6 +7,7 @@ | |||
"fav": true, | |||
"canMarkAsFavourite": true, | |||
"canBulkChange": true, | |||
"canCreateManualIssue": true, | |||
"periods": [], | |||
"severities": [], | |||
"rules": [], |
@@ -8,6 +8,7 @@ | |||
"fav": false, | |||
"canMarkAsFavourite": false, | |||
"canBulkChange": false, | |||
"canCreateManualIssue": false, | |||
"periods": [], | |||
"severities": [], | |||
"rules": [], |
@@ -8,6 +8,7 @@ | |||
"fav": false, | |||
"canMarkAsFavourite": false, | |||
"canBulkChange": false, | |||
"canCreateManualIssue": false, | |||
"periods": [], | |||
"severities": [], | |||
"rules": [], |
@@ -6,6 +6,7 @@ | |||
"fav": true, | |||
"canMarkAsFavourite": true, | |||
"canBulkChange": true, | |||
"canCreateManualIssue": true, | |||
"periods": [], | |||
"severities": [], | |||
"rules": [], |