private String language;
private Long projectId;
private Long subProjectId;
+ private boolean enabled = true;
public Long getId() {
return id;
return this;
}
+ public boolean isEnabled() {
+ return enabled;
+ }
+
+ public ComponentDto setEnabled(boolean enabled) {
+ this.enabled = enabled;
+ return this;
+ }
+
@Override
public String getKey() {
return kee;
ComponentDto selectById(long id);
long countById(long id);
+
+ void insert(ComponentDto rule);
}
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>
@Override
protected ComponentDto doInsert(DbSession session, ComponentDto item) {
- throw notImplemented();
+ getMapper(session).insert(item);
+ return item;
}
@Override
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");
}
}
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;
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;
.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)
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);
appendMeasures(json, measuresByMetricKey, severitiesAggregation, periodIndex);
appendTabs(json, measuresByMetricKey);
appendExtensions(json, component, userSession);
+ appendManualRules(json);
} finally {
MyBatis.closeQuietly(session);
}
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());
}
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) {
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);
return measuresByMetricKey;
}
-
@CheckForNull
private Date periodDate(@Nullable final Integer periodIndex, List<Period> periodList) {
if (periodIndex != null) {
+++ /dev/null
-{
- "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": true,
- "canMarkAsFavourite": true,
- "canBulkChange": false,
- "periods": [
- [
- 1,
- "since previous analysis (2014 May 08)",
- "2014-05-08T23:40:12+0200"
- ],
- [
- 2,
- "over 365 days (2013 May 17)",
- "2013-05-17T23:52:45+0200"
- ],
- [
- 3,
- "since previous version (4.3 - 2014 Apr 17)",
- "2014-04-17T23:34:08+0200"
- ]
- ],
- "severities": [
- [
- "INFO",
- "Info",
- 4
- ]
- ],
- "rules": [
- [
- "squid:S1133",
- "Deprecated code should be removed eventually",
- 4
- ]
- ],
- "measures": {
- "fNcloc": "12",
- "fDebt": "4h",
- "fIssues": "4",
- "fInfoIssues": "4"
- },
- "extensions": [
- ["metricsTab", "Metrics"]
- ],
- "tabs": [
- "scn", "coverage", "duplications"
- ]
-}
--- /dev/null
+{
+ "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": true,
+ "canMarkAsFavourite": true,
+ "canBulkChange": false,
+ "canCreateManualIssue": false,
+ "periods": [
+ [
+ 1,
+ "since previous analysis (2014 May 08)",
+ "2014-05-08T23:40:12+0200"
+ ],
+ [
+ 2,
+ "over 365 days (2013 May 17)",
+ "2013-05-17T23:52:45+0200"
+ ],
+ [
+ 3,
+ "since previous version (4.3 - 2014 Apr 17)",
+ "2014-04-17T23:34:08+0200"
+ ]
+ ],
+ "severities": [
+ [
+ "INFO",
+ "Info",
+ 4
+ ]
+ ],
+ "rules": [
+ [
+ "squid:S1133",
+ "Deprecated code should be removed eventually",
+ 4
+ ]
+ ],
+ "measures": {
+ "fNcloc": "12",
+ "fDebt": "4h",
+ "fIssues": "4",
+ "fInfoIssues": "4"
+ },
+ "extensions": [
+ ["metricsTab", "Metrics"]
+ ],
+ "tabs": [
+ "scn", "coverage", "duplications"
+ ],
+ "manual_rules": [
+ {"key": "manual:API", "name": "API"}
+ ]
+}
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 {
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
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);
+ }
}
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;
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;
@Mock
Views views;
+ @Mock
+ RuleService ruleService;
+
@Mock
Periods periods;
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
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);
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;
@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");
}
--- /dev/null
+<dataset>
+
+</dataset>
--- /dev/null
+<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>
"fav": true,
"canMarkAsFavourite": true,
"canBulkChange": true,
+ "canCreateManualIssue": true,
"periods": [],
"severities": [],
"rules": [],
"fav": false,
"canMarkAsFavourite": false,
"canBulkChange": false,
+ "canCreateManualIssue": false,
"periods": [],
"severities": [],
"rules": [],
"fav": false,
"canMarkAsFavourite": false,
"canBulkChange": false,
+ "canCreateManualIssue": false,
"periods": [],
"severities": [],
"rules": [],
"fav": false,
"canMarkAsFavourite": false,
"canBulkChange": false,
+ "canCreateManualIssue": false,
"periods": [],
"severities": [
["BLOCKER", "Blocker", 1],
"fav": false,
"canMarkAsFavourite": false,
"canBulkChange": false,
+ "canCreateManualIssue": false,
"periods": [
[1, "since previous analysis (May 08 2014)", "2014-05-08T00:00:00+0200"]
],
--- /dev/null
+{
+ "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"}
+ ]
+}
"fav": false,
"canMarkAsFavourite": false,
"canBulkChange": false,
+ "canCreateManualIssue": false,
"periods": [],
"severities": [],
"rules": [],
"fav": false,
"canMarkAsFavourite": false,
"canBulkChange": false,
+ "canCreateManualIssue": false,
"periods": [
[1, "since previous analysis (May 08 2014)", "2014-05-08T00:00:00+0200"]
],
"fav": false,
"canMarkAsFavourite": false,
"canBulkChange": false,
+ "canCreateManualIssue": false,
"periods": [
[1, "since previous analysis (May 08 2014)", "2014-05-08T00:00:00+0200"]
],
"fav": false,
"canMarkAsFavourite": false,
"canBulkChange": false,
+ "canCreateManualIssue": false,
"periods": [],
"severities": [],
"rules": [
"fav": false,
"canMarkAsFavourite": false,
"canBulkChange": false,
+ "canCreateManualIssue": false,
"periods": [
[1, "since previous analysis (May 08 2014)", "2014-05-08T00:00:00+0200"]
],
"fav": false,
"canMarkAsFavourite": false,
"canBulkChange": false,
+ "canCreateManualIssue": false,
"periods": [],
"severities": [
["MAJOR", "Major", 5]
"fav": false,
"canMarkAsFavourite": false,
"canBulkChange": false,
+ "canCreateManualIssue": false,
"periods": [
[1, "since previous analysis (May 08 2014)", "2014-05-08T00:00:00+0200"]
],
"fav": true,
"canMarkAsFavourite": true,
"canBulkChange": true,
+ "canCreateManualIssue": true,
"periods": [],
"severities": [],
"rules": [],
"fav": false,
"canMarkAsFavourite": false,
"canBulkChange": false,
+ "canCreateManualIssue": false,
"periods": [],
"severities": [],
"rules": [],
"fav": false,
"canMarkAsFavourite": false,
"canBulkChange": false,
+ "canCreateManualIssue": false,
"periods": [],
"severities": [],
"rules": [],
"fav": true,
"canMarkAsFavourite": true,
"canBulkChange": true,
+ "canCreateManualIssue": true,
"periods": [],
"severities": [],
"rules": [],