aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJulien Lancelot <julien.lancelot@gmail.com>2013-05-03 09:37:55 +0200
committerJulien Lancelot <julien.lancelot@gmail.com>2013-05-03 09:38:16 +0200
commit53c455995c5d49b2876f334a98ddc86bbdf1c896 (patch)
tree718d726bacb3e227a79ca2757edd8a6510976d0b
parent6194a1a3e19cd026da8806ccf84ebc8a22524b1d (diff)
downloadsonarqube-53c455995c5d49b2876f334a98ddc86bbdf1c896.tar.gz
sonarqube-53c455995c5d49b2876f334a98ddc86bbdf1c896.zip
SONAR-3755 Return Action plans in Issue Finder
-rw-r--r--sonar-core/src/main/java/org/sonar/core/issue/DefaultActionPlan.java123
-rw-r--r--sonar-core/src/main/java/org/sonar/core/issue/DefaultIssue.java13
-rw-r--r--sonar-core/src/main/java/org/sonar/core/issue/db/ActionPlanIssueDao.java68
-rw-r--r--sonar-core/src/main/java/org/sonar/core/issue/db/ActionPlanIssueDto.java178
-rw-r--r--sonar-core/src/main/java/org/sonar/core/issue/db/ActionPlanIssueMapper.java37
-rw-r--r--sonar-core/src/main/java/org/sonar/core/persistence/DaoUtils.java2
-rw-r--r--sonar-core/src/main/java/org/sonar/core/persistence/DatabaseVersion.java2
-rw-r--r--sonar-core/src/main/java/org/sonar/core/persistence/MyBatis.java7
-rw-r--r--sonar-core/src/main/resources/org/sonar/core/issue/db/ActionPlanIssueMapper.xml31
-rw-r--r--sonar-core/src/main/resources/org/sonar/core/persistence/rows-h2.sql1
-rw-r--r--sonar-core/src/main/resources/org/sonar/core/persistence/schema-h2.ddl1
-rw-r--r--sonar-core/src/test/java/org/sonar/core/issue/db/ActionPlanIssueDaoTest.java49
-rw-r--r--sonar-core/src/test/resources/org/sonar/core/issue/db/ActionPlanIssueDaoTest/should_find_by_issue_ids.xml19
-rw-r--r--sonar-plugin-api/src/main/java/org/sonar/api/issue/ActionPlan.java56
-rw-r--r--sonar-plugin-api/src/main/java/org/sonar/api/issue/Issue.java5
-rw-r--r--sonar-plugin-api/src/main/java/org/sonar/api/issue/IssueFinder.java4
-rw-r--r--sonar-server/src/main/java/org/sonar/server/issue/ServerIssueFinder.java58
-rw-r--r--sonar-server/src/main/webapp/WEB-INF/db/migrate/394_add_key_to_action_plan.rb38
-rw-r--r--sonar-server/src/test/java/org/sonar/server/issue/ServerIssueFinderTest.java39
19 files changed, 716 insertions, 15 deletions
diff --git a/sonar-core/src/main/java/org/sonar/core/issue/DefaultActionPlan.java b/sonar-core/src/main/java/org/sonar/core/issue/DefaultActionPlan.java
new file mode 100644
index 00000000000..2ecd6a9b745
--- /dev/null
+++ b/sonar-core/src/main/java/org/sonar/core/issue/DefaultActionPlan.java
@@ -0,0 +1,123 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2013 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube 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.
+ *
+ * SonarQube 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.core.issue;
+
+import org.sonar.api.issue.ActionPlan;
+
+import java.util.Date;
+import java.util.UUID;
+
+public class DefaultActionPlan implements ActionPlan {
+
+ private String key;
+ private String name;
+ private String description;
+ private String userLogin;
+ private String status;
+ private Date deadLine;
+ private Date creationDate;
+ private Date updateDate;
+
+ private DefaultActionPlan(){
+
+ }
+
+ public static DefaultActionPlan create(String name) {
+ DefaultActionPlan actionPlan = new DefaultActionPlan();
+ actionPlan.setKey(UUID.randomUUID().toString());
+ Date now = new Date();
+ actionPlan.setName(name);
+ actionPlan.setCreationDate(now).setUpdateDate(now);
+ return actionPlan;
+ }
+
+ public String key() {
+ return key;
+ }
+
+ public DefaultActionPlan setKey(String key) {
+ this.key = key;
+ return this;
+ }
+
+ public String name() {
+ return name;
+ }
+
+ public DefaultActionPlan setName(String name) {
+ this.name = name;
+ return this;
+ }
+
+ public String description() {
+ return description;
+ }
+
+ public DefaultActionPlan setDescription(String description) {
+ this.description = description;
+ return this;
+ }
+
+ public String userLogin() {
+ return userLogin;
+ }
+
+ public DefaultActionPlan setUserLogin(String userLogin) {
+ this.userLogin = userLogin;
+ return this;
+ }
+
+ public String status() {
+ return status;
+ }
+
+ public DefaultActionPlan setStatus(String status) {
+ this.status = status;
+ return this;
+ }
+
+ public Date deadLine() {
+ return deadLine;
+ }
+
+ public DefaultActionPlan setDeadLine(Date deadLine) {
+ this.deadLine = deadLine;
+ return this;
+ }
+
+ public Date creationDate() {
+ return creationDate;
+ }
+
+ public DefaultActionPlan setCreationDate(Date creationDate) {
+ this.creationDate = creationDate;
+ return this;
+ }
+
+ public Date updateDate() {
+ return updateDate;
+ }
+
+ public DefaultActionPlan setUpdateDate(Date updateDate) {
+ this.updateDate = updateDate;
+ return this;
+ }
+}
diff --git a/sonar-core/src/main/java/org/sonar/core/issue/DefaultIssue.java b/sonar-core/src/main/java/org/sonar/core/issue/DefaultIssue.java
index fdd12ebfa37..86eefaed14a 100644
--- a/sonar-core/src/main/java/org/sonar/core/issue/DefaultIssue.java
+++ b/sonar-core/src/main/java/org/sonar/core/issue/DefaultIssue.java
@@ -28,12 +28,14 @@ import com.google.common.collect.Maps;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang.builder.ToStringBuilder;
import org.apache.commons.lang.builder.ToStringStyle;
+import org.sonar.api.issue.ActionPlan;
import org.sonar.api.issue.Issue;
import org.sonar.api.rule.RuleKey;
import org.sonar.api.rule.Severity;
import javax.annotation.CheckForNull;
import javax.annotation.Nullable;
+
import java.io.Serializable;
import java.util.Collections;
import java.util.Date;
@@ -61,6 +63,7 @@ public class DefaultIssue implements Issue {
private Map<String, String> attributes = null;
private String authorLogin = null;
private FieldDiffs diffs = null;
+ private List<ActionPlan> actionPlans = null;
private List<IssueComment> newComments = null;
// functional dates
@@ -315,6 +318,16 @@ public class DefaultIssue implements Issue {
return this;
}
+ @CheckForNull
+ public List<ActionPlan> actionPlans() {
+ return actionPlans;
+ }
+
+ public DefaultIssue setActionPlans(List<ActionPlan> actionPlans) {
+ this.actionPlans = actionPlans;
+ return this;
+ }
+
public DefaultIssue setFieldDiff(IssueChangeContext context, String field, @Nullable Serializable oldValue, @Nullable Serializable newValue) {
if (!Objects.equal(oldValue, newValue)) {
if (diffs == null) {
diff --git a/sonar-core/src/main/java/org/sonar/core/issue/db/ActionPlanIssueDao.java b/sonar-core/src/main/java/org/sonar/core/issue/db/ActionPlanIssueDao.java
new file mode 100644
index 00000000000..9366553fbb9
--- /dev/null
+++ b/sonar-core/src/main/java/org/sonar/core/issue/db/ActionPlanIssueDao.java
@@ -0,0 +1,68 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2013 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube 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.
+ *
+ * SonarQube 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.core.issue.db;
+
+import com.google.common.annotations.VisibleForTesting;
+import com.google.common.collect.Lists;
+import org.apache.ibatis.session.SqlSession;
+import org.sonar.api.BatchComponent;
+import org.sonar.api.ServerComponent;
+import org.sonar.core.persistence.MyBatis;
+
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+
+import static com.google.common.collect.Lists.newArrayList;
+
+/**
+ * @since 3.6
+ */
+public class ActionPlanIssueDao implements BatchComponent, ServerComponent {
+
+ private final MyBatis mybatis;
+
+ public ActionPlanIssueDao(MyBatis mybatis) {
+ this.mybatis = mybatis;
+ }
+
+ public Collection<ActionPlanIssueDto> findByIssueIds(Collection<Long> issueIds, SqlSession session) {
+ if (issueIds.isEmpty()) {
+ return Collections.emptyList();
+ }
+ try {
+ List<List<Long>> idsPartition = Lists.partition(newArrayList(issueIds), 1000);
+ return session.getMapper(ActionPlanIssueMapper.class).findByIssueIds(idsPartition);
+ } finally {
+ MyBatis.closeQuietly(session);
+ }
+ }
+
+ @VisibleForTesting
+ Collection<ActionPlanIssueDto> findByIssueIds(Collection<Long> issueIds) {
+ SqlSession session = mybatis.openSession();
+ try {
+ return findByIssueIds(issueIds, session);
+ } finally {
+ MyBatis.closeQuietly(session);
+ }
+ }
+}
diff --git a/sonar-core/src/main/java/org/sonar/core/issue/db/ActionPlanIssueDto.java b/sonar-core/src/main/java/org/sonar/core/issue/db/ActionPlanIssueDto.java
new file mode 100644
index 00000000000..988f4d39962
--- /dev/null
+++ b/sonar-core/src/main/java/org/sonar/core/issue/db/ActionPlanIssueDto.java
@@ -0,0 +1,178 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2013 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube 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.
+ *
+ * SonarQube 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.core.issue.db;
+
+import org.apache.commons.lang.builder.ToStringBuilder;
+import org.apache.commons.lang.builder.ToStringStyle;
+import org.sonar.core.issue.DefaultActionPlan;
+
+import java.util.Date;
+
+/**
+ * @since 3.6
+ */
+public class ActionPlanIssueDto {
+
+ private Integer id;
+ private String kee;
+ private String name;
+ private String description;
+ private String userLogin;
+ private Integer projectId;
+ private String status;
+ private Date deadLine;
+ private Date createdAt;
+ private Date updatedAt;
+ private Long issueId;
+
+ public Integer getId() {
+ return id;
+ }
+
+ public ActionPlanIssueDto setId(Integer id) {
+ this.id = id;
+ return this;
+ }
+
+ public String getKee() {
+ return kee;
+ }
+
+ public ActionPlanIssueDto setKee(String kee) {
+ this.kee = kee;
+ return this;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public ActionPlanIssueDto setName(String name) {
+ this.name = name;
+ return this;
+ }
+
+ public String getDescription() {
+ return description;
+ }
+
+ public ActionPlanIssueDto setDescription(String description) {
+ this.description = description;
+ return this;
+ }
+
+ public String getUserLogin() {
+ return userLogin;
+ }
+
+ public ActionPlanIssueDto setUserLogin(String userLogin) {
+ this.userLogin = userLogin;
+ return this;
+ }
+
+ public Integer getProjectId() {
+ return projectId;
+ }
+
+ public ActionPlanIssueDto setProjectId(Integer projectId) {
+ this.projectId = projectId;
+ return this;
+ }
+
+ public String getStatus() {
+ return status;
+ }
+
+ public ActionPlanIssueDto setStatus(String status) {
+ this.status = status;
+ return this;
+ }
+
+ public Date getDeadLine() {
+ return deadLine;
+ }
+
+ public ActionPlanIssueDto setDeadLine(Date deadLine) {
+ this.deadLine = deadLine;
+ return this;
+ }
+
+ public Date getCreatedAt() {
+ return createdAt;
+ }
+
+ public ActionPlanIssueDto setCreatedAt(Date createdAt) {
+ this.createdAt = createdAt;
+ return this;
+ }
+
+ public Date getUpdatedAt() {
+ return updatedAt;
+ }
+
+ public ActionPlanIssueDto setUpdatedAt(Date updatedAt) {
+ this.updatedAt = updatedAt;
+ return this;
+ }
+
+ public Long getIssueId() {
+ return issueId;
+ }
+
+ public ActionPlanIssueDto setIssueId(Long issueId) {
+ this.issueId = issueId;
+ return this;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+
+ ActionPlanIssueDto actionPlanIssueDto = (ActionPlanIssueDto) o;
+ return !(id != null ? !id.equals(actionPlanIssueDto.id) : actionPlanIssueDto.id != null);
+ }
+
+ @Override
+ public int hashCode() {
+ return id != null ? id.hashCode() : 0;
+ }
+
+ @Override
+ public String toString() {
+ return ToStringBuilder.reflectionToString(this, ToStringStyle.SHORT_PREFIX_STYLE);
+ }
+
+ public DefaultActionPlan toActionPlan() {
+ return DefaultActionPlan.create(name)
+ .setKey(kee)
+ .setDescription(description)
+ .setStatus(status)
+ .setDeadLine(deadLine)
+ .setUserLogin(userLogin)
+ .setCreationDate(createdAt)
+ .setUpdateDate(updatedAt);
+ }
+}
diff --git a/sonar-core/src/main/java/org/sonar/core/issue/db/ActionPlanIssueMapper.java b/sonar-core/src/main/java/org/sonar/core/issue/db/ActionPlanIssueMapper.java
new file mode 100644
index 00000000000..6814ea2c797
--- /dev/null
+++ b/sonar-core/src/main/java/org/sonar/core/issue/db/ActionPlanIssueMapper.java
@@ -0,0 +1,37 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2013 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube 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.
+ *
+ * SonarQube 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.core.issue.db;
+
+import org.apache.ibatis.annotations.Param;
+
+import java.util.Collection;
+import java.util.List;
+
+/**
+ * @since 3.6
+ */
+public interface ActionPlanIssueMapper {
+
+ /**
+ * @since3.6
+ */
+ Collection<ActionPlanIssueDto> findByIssueIds(@Param("issueIds") List <List<Long>> issueIds);
+}
diff --git a/sonar-core/src/main/java/org/sonar/core/persistence/DaoUtils.java b/sonar-core/src/main/java/org/sonar/core/persistence/DaoUtils.java
index dbb79e2a583..05022a52d6b 100644
--- a/sonar-core/src/main/java/org/sonar/core/persistence/DaoUtils.java
+++ b/sonar-core/src/main/java/org/sonar/core/persistence/DaoUtils.java
@@ -24,6 +24,7 @@ import org.sonar.core.dashboard.ActiveDashboardDao;
import org.sonar.core.dashboard.DashboardDao;
import org.sonar.core.duplication.DuplicationDao;
import org.sonar.core.graph.jdbc.GraphDao;
+import org.sonar.core.issue.db.ActionPlanIssueDao;
import org.sonar.core.issue.db.IssueChangeDao;
import org.sonar.core.issue.db.IssueDao;
import org.sonar.core.measure.MeasureFilterDao;
@@ -51,6 +52,7 @@ public final class DaoUtils {
@SuppressWarnings("unchecked")
public static List<Class<?>> getDaoClasses() {
return ImmutableList.of(
+ ActionPlanIssueDao.class,
ActiveDashboardDao.class,
AuthorDao.class,
AuthorizationDao.class,
diff --git a/sonar-core/src/main/java/org/sonar/core/persistence/DatabaseVersion.java b/sonar-core/src/main/java/org/sonar/core/persistence/DatabaseVersion.java
index 7b6db8321bf..389a2425c22 100644
--- a/sonar-core/src/main/java/org/sonar/core/persistence/DatabaseVersion.java
+++ b/sonar-core/src/main/java/org/sonar/core/persistence/DatabaseVersion.java
@@ -32,7 +32,7 @@ import java.util.List;
*/
public class DatabaseVersion implements BatchComponent, ServerComponent {
- public static final int LAST_VERSION = 393;
+ public static final int LAST_VERSION = 394;
public static enum Status {
UP_TO_DATE, REQUIRES_UPGRADE, REQUIRES_DOWNGRADE, FRESH_INSTALL
diff --git a/sonar-core/src/main/java/org/sonar/core/persistence/MyBatis.java b/sonar-core/src/main/java/org/sonar/core/persistence/MyBatis.java
index e89aa600365..a0173a82950 100644
--- a/sonar-core/src/main/java/org/sonar/core/persistence/MyBatis.java
+++ b/sonar-core/src/main/java/org/sonar/core/persistence/MyBatis.java
@@ -44,9 +44,7 @@ import org.sonar.core.duplication.DuplicationMapper;
import org.sonar.core.duplication.DuplicationUnitDto;
import org.sonar.core.graph.jdbc.GraphDto;
import org.sonar.core.graph.jdbc.GraphDtoMapper;
-import org.sonar.core.issue.db.IssueChangeDto;
-import org.sonar.core.issue.db.IssueChangeMapper;
-import org.sonar.core.issue.db.IssueDto;
+import org.sonar.core.issue.db.*;
import org.sonar.core.measure.MeasureFilterDto;
import org.sonar.core.measure.MeasureFilterMapper;
import org.sonar.core.properties.PropertiesMapper;
@@ -123,6 +121,7 @@ public class MyBatis implements BatchComponent, ServerComponent {
loadAlias(conf, "MeasureData", MeasureData.class);
loadAlias(conf, "Issue", IssueDto.class);
loadAlias(conf, "IssueChange", IssueChangeDto.class);
+ loadAlias(conf, "ActionPlanIssue", ActionPlanIssueDto.class);
loadAlias(conf, "SnapshotData", SnapshotDataDto.class);
Class<?>[] mappers = {ActiveDashboardMapper.class, AuthorMapper.class, DashboardMapper.class,
@@ -130,7 +129,7 @@ public class MyBatis implements BatchComponent, ServerComponent {
MeasureFilterMapper.class, PropertiesMapper.class, PurgeMapper.class, ResourceKeyUpdaterMapper.class, ResourceIndexerMapper.class, ResourceMapper.class,
ResourceSnapshotMapper.class, ReviewCommentMapper.class, ReviewMapper.class, RoleMapper.class, RuleMapper.class, SchemaMigrationMapper.class,
SemaphoreMapper.class, UserMapper.class, WidgetMapper.class, WidgetPropertyMapper.class, MeasureMapper.class, SnapshotDataMapper.class,
- SnapshotSourceMapper.class
+ SnapshotSourceMapper.class, ActionPlanIssueMapper.class
};
loadMappers(conf, mappers);
loadMapper(conf, "org.sonar.core.issue.db.IssueMapper");
diff --git a/sonar-core/src/main/resources/org/sonar/core/issue/db/ActionPlanIssueMapper.xml b/sonar-core/src/main/resources/org/sonar/core/issue/db/ActionPlanIssueMapper.xml
new file mode 100644
index 00000000000..edd28f16180
--- /dev/null
+++ b/sonar-core/src/main/resources/org/sonar/core/issue/db/ActionPlanIssueMapper.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+
+<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mappei.dtd">
+
+<mapper namespace="org.sonar.core.issue.db.ActionPlanIssueMapper">
+
+ <sql id="actionPlanColumns">
+ ap.id,
+ ap.name as name,
+ ap.description as description,
+ ap.user_login as userLogin,
+ ap.project_id as projectId,
+ ap.status as status,
+ ap.deadline as deadLine,
+ ap.created_at as createdAt,
+ ap.updated_at as updatedAt
+ </sql>
+
+ <select id="findByIssueIds" parameterType="long" resultType="ActionPlanIssue">
+ select <include refid="actionPlanColumns"/>, api.issue_id as issueId
+ from action_plans_issues api left outer join action_plans ap on ap.id = api.action_plan_id
+ <where>
+ <foreach collection="issueIds" open="api.issue_id in (" close=")" item="list" separator=") or api.issue_id in (" >
+ <foreach collection="list" item="element" separator=",">
+ #{element}
+ </foreach>
+ </foreach>
+ </where>
+ </select>
+
+</mapper> \ No newline at end of file
diff --git a/sonar-core/src/main/resources/org/sonar/core/persistence/rows-h2.sql b/sonar-core/src/main/resources/org/sonar/core/persistence/rows-h2.sql
index 1ea246041f5..65d8f228d19 100644
--- a/sonar-core/src/main/resources/org/sonar/core/persistence/rows-h2.sql
+++ b/sonar-core/src/main/resources/org/sonar/core/persistence/rows-h2.sql
@@ -161,6 +161,7 @@ INSERT INTO SCHEMA_MIGRATIONS(VERSION) VALUES ('390');
INSERT INTO SCHEMA_MIGRATIONS(VERSION) VALUES ('391');
INSERT INTO SCHEMA_MIGRATIONS(VERSION) VALUES ('392');
INSERT INTO SCHEMA_MIGRATIONS(VERSION) VALUES ('393');
+INSERT INTO SCHEMA_MIGRATIONS(VERSION) VALUES ('394');
INSERT INTO USERS(ID, LOGIN, NAME, EMAIL, CRYPTED_PASSWORD, SALT, CREATED_AT, UPDATED_AT, REMEMBER_TOKEN, REMEMBER_TOKEN_EXPIRES_AT) VALUES (1, 'admin', 'Administrator', '', 'a373a0e667abb2604c1fd571eb4ad47fe8cc0878', '48bc4b0d93179b5103fd3885ea9119498e9d161b', '2011-09-26 22:27:48.0', '2011-09-26 22:27:48.0', null, null);
ALTER TABLE USERS ALTER COLUMN ID RESTART WITH 2;
diff --git a/sonar-core/src/main/resources/org/sonar/core/persistence/schema-h2.ddl b/sonar-core/src/main/resources/org/sonar/core/persistence/schema-h2.ddl
index 5067e167ea3..b02576d6933 100644
--- a/sonar-core/src/main/resources/org/sonar/core/persistence/schema-h2.ddl
+++ b/sonar-core/src/main/resources/org/sonar/core/persistence/schema-h2.ddl
@@ -435,6 +435,7 @@ CREATE TABLE "RESOURCE_INDEX" (
CREATE TABLE "ACTION_PLANS" (
"ID" INTEGER NOT NULL GENERATED BY DEFAULT AS IDENTITY (START WITH 1, INCREMENT BY 1),
"USER_ID" INTEGER,
+ "KEE" VARCHAR(100),
"NAME" VARCHAR(200),
"DESCRIPTION" VARCHAR(1000),
"DEADLINE" TIMESTAMP,
diff --git a/sonar-core/src/test/java/org/sonar/core/issue/db/ActionPlanIssueDaoTest.java b/sonar-core/src/test/java/org/sonar/core/issue/db/ActionPlanIssueDaoTest.java
new file mode 100644
index 00000000000..8ac11e29428
--- /dev/null
+++ b/sonar-core/src/test/java/org/sonar/core/issue/db/ActionPlanIssueDaoTest.java
@@ -0,0 +1,49 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2013 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube 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.
+ *
+ * SonarQube 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.core.issue.db;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.sonar.core.persistence.AbstractDaoTestCase;
+
+import java.util.Collection;
+
+import static com.google.common.collect.Lists.newArrayList;
+import static org.fest.assertions.Assertions.assertThat;
+
+public class ActionPlanIssueDaoTest extends AbstractDaoTestCase {
+
+ private ActionPlanIssueDao dao;
+
+ @Before
+ public void createDao() {
+ dao = new ActionPlanIssueDao(getMyBatis());
+ }
+
+ @Test
+ public void should_find_by_issue_ids() {
+ setupData("should_find_by_issue_ids");
+
+ Collection<ActionPlanIssueDto> result = dao.findByIssueIds(newArrayList(250l, 251l, 252l));
+ assertThat(result).hasSize(3);
+ assertThat(result.iterator().next().getIssueId()).isNotNull();
+ }
+}
diff --git a/sonar-core/src/test/resources/org/sonar/core/issue/db/ActionPlanIssueDaoTest/should_find_by_issue_ids.xml b/sonar-core/src/test/resources/org/sonar/core/issue/db/ActionPlanIssueDaoTest/should_find_by_issue_ids.xml
new file mode 100644
index 00000000000..b68c783aa1a
--- /dev/null
+++ b/sonar-core/src/test/resources/org/sonar/core/issue/db/ActionPlanIssueDaoTest/should_find_by_issue_ids.xml
@@ -0,0 +1,19 @@
+<dataset>
+
+ <action_plans id="1" project_id="1" name="SHORT_TERM" description="[null]" deadline="[null]"
+ user_login="igor" status="[null]" created_at="[null]" updated_at="[null]" />
+
+ <action_plans_issues action_plan_id="1" issue_id="250" />
+
+ <action_plans id="2" project_id="1" name="SHORT_TERM" description="[null]" deadline="[null]"
+ user_login="igor" status="[null]" created_at="[null]" updated_at="[null]" />
+
+ <action_plans_issues action_plan_id="2" issue_id="251" />
+ <action_plans_issues action_plan_id="2" issue_id="252" />
+
+ <action_plans id="3" project_id="1" name="SHORT_TERM" description="[null]" deadline="[null]"
+ user_login="igor" status="[null]" created_at="[null]" updated_at="[null]" />
+
+ <action_plans_issues action_plan_id="3" issue_id="253" />
+
+</dataset>
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/issue/ActionPlan.java b/sonar-plugin-api/src/main/java/org/sonar/api/issue/ActionPlan.java
new file mode 100644
index 00000000000..770bb0c7d32
--- /dev/null
+++ b/sonar-plugin-api/src/main/java/org/sonar/api/issue/ActionPlan.java
@@ -0,0 +1,56 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2013 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube 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.
+ *
+ * SonarQube 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.api.issue;
+
+import javax.annotation.CheckForNull;
+
+import java.io.Serializable;
+import java.util.Date;
+
+/**
+ * @since 3.6
+ */
+public interface ActionPlan extends Serializable {
+
+ /**
+ * Unique generated key
+ */
+ String key();
+
+ String name();
+
+ @CheckForNull
+ String description();
+
+ @CheckForNull
+ String userLogin();
+
+ @CheckForNull
+ String status();
+
+ @CheckForNull
+ Date deadLine() ;
+
+ Date creationDate();
+
+ Date updateDate();
+
+}
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/issue/Issue.java b/sonar-plugin-api/src/main/java/org/sonar/api/issue/Issue.java
index 775e919c599..6f3de886588 100644
--- a/sonar-plugin-api/src/main/java/org/sonar/api/issue/Issue.java
+++ b/sonar-plugin-api/src/main/java/org/sonar/api/issue/Issue.java
@@ -22,8 +22,10 @@ package org.sonar.api.issue;
import org.sonar.api.rule.RuleKey;
import javax.annotation.CheckForNull;
+
import java.io.Serializable;
import java.util.Date;
+import java.util.List;
import java.util.Map;
/**
@@ -87,4 +89,7 @@ public interface Issue extends Serializable {
@CheckForNull
String authorLogin();
+ @CheckForNull
+ List<ActionPlan> actionPlans();
+
}
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/issue/IssueFinder.java b/sonar-plugin-api/src/main/java/org/sonar/api/issue/IssueFinder.java
index 70086f1b245..7ba13797017 100644
--- a/sonar-plugin-api/src/main/java/org/sonar/api/issue/IssueFinder.java
+++ b/sonar-plugin-api/src/main/java/org/sonar/api/issue/IssueFinder.java
@@ -49,6 +49,10 @@ public interface IssueFinder extends ServerComponent {
Collection<Component> components();
+ Collection<ActionPlan> actionPlans(Issue issue);
+
+ Collection<ActionPlan> actionPlans();
+
Paging paging();
boolean securityExclusions();
diff --git a/sonar-server/src/main/java/org/sonar/server/issue/ServerIssueFinder.java b/sonar-server/src/main/java/org/sonar/server/issue/ServerIssueFinder.java
index 72423b91ef8..01402ad4217 100644
--- a/sonar-server/src/main/java/org/sonar/server/issue/ServerIssueFinder.java
+++ b/sonar-server/src/main/java/org/sonar/server/issue/ServerIssueFinder.java
@@ -20,17 +20,19 @@
package org.sonar.server.issue;
import com.google.common.base.Predicate;
+import com.google.common.collect.ArrayListMultimap;
import com.google.common.collect.Iterables;
+import com.google.common.collect.ListMultimap;
import com.google.common.collect.Sets;
import org.apache.ibatis.session.SqlSession;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.sonar.api.component.Component;
-import org.sonar.api.issue.Issue;
-import org.sonar.api.issue.IssueFinder;
-import org.sonar.api.issue.IssueQuery;
-import org.sonar.api.issue.Paging;
+import org.sonar.api.issue.*;
import org.sonar.api.rules.Rule;
+import org.sonar.core.issue.DefaultIssue;
+import org.sonar.core.issue.db.ActionPlanIssueDao;
+import org.sonar.core.issue.db.ActionPlanIssueDto;
import org.sonar.core.issue.db.IssueDao;
import org.sonar.core.issue.db.IssueDto;
import org.sonar.core.persistence.MyBatis;
@@ -59,13 +61,15 @@ public class ServerIssueFinder implements IssueFinder {
private final AuthorizationDao authorizationDao;
private final DefaultRuleFinder ruleFinder;
private final ResourceDao resourceDao;
+ private final ActionPlanIssueDao actionPlanIssueDao;
- public ServerIssueFinder(MyBatis myBatis, IssueDao issueDao, AuthorizationDao authorizationDao, DefaultRuleFinder ruleFinder, ResourceDao resourceDao) {
+ public ServerIssueFinder(MyBatis myBatis, IssueDao issueDao, AuthorizationDao authorizationDao, DefaultRuleFinder ruleFinder, ResourceDao resourceDao, ActionPlanIssueDao actionPlanIssueDao) {
this.myBatis = myBatis;
this.issueDao = issueDao;
this.authorizationDao = authorizationDao;
this.ruleFinder = ruleFinder;
this.resourceDao = resourceDao;
+ this.actionPlanIssueDao = actionPlanIssueDao;
}
public Results find(IssueQuery query, @Nullable Integer currentUserId, String role) {
@@ -82,14 +86,24 @@ public class ServerIssueFinder implements IssueFinder {
Collection<IssueDto> dtos = issueDao.selectByIds(paginatedAuthorizedIssueIds, sqlSession);
Set<Integer> ruleIds = Sets.newLinkedHashSet();
List<Issue> issues = newArrayList();
+ List<Long> issueIds = newArrayList();
+ Map<Long, Issue> issuesById = newHashMap();
for (IssueDto dto : dtos) {
if (authorizedComponentIds.contains(dto.getResourceId())) {
- issues.add(dto.toDefaultIssue());
+ DefaultIssue defaultIssue = dto.toDefaultIssue();
+ issuesById.put(dto.getId(), defaultIssue);
+ issueIds.add(dto.getId());
+ issues.add(defaultIssue);
ruleIds.add(dto.getRuleId());
}
}
- return new DefaultResults(issues, getRulesByIssue(issues, ruleIds), getComponentsByIssue(issues, componentIds), paging, authorizedIssues.size() != allIssuesDto.size());
+ Collection<ActionPlanIssueDto> actionPlanIssueDtos = actionPlanIssueDao.findByIssueIds(issueIds, sqlSession);
+ ListMultimap<Issue, ActionPlan> actionPlansByIssueKey = createActionPlansByIssue(actionPlanIssueDtos, issuesById);
+ setActionPlans(issues, actionPlansByIssueKey);
+
+ return new DefaultResults(issues, getRulesByIssue(issues, ruleIds), getComponentsByIssue(issues, componentIds), actionPlansByIssueKey,
+ paging, authorizedIssues.size() != allIssuesDto.size());
} finally {
MyBatis.closeQuietly(sqlSession);
}
@@ -162,6 +176,23 @@ public class ServerIssueFinder implements IssueFinder {
}, null);
}
+ private ListMultimap createActionPlansByIssue(Collection<ActionPlanIssueDto> actionPlanIssueDtos, Map<Long, Issue> issuesById) {
+ ListMultimap<Issue, ActionPlan> actionPlansByIssue = ArrayListMultimap.create();
+ for (ActionPlanIssueDto actionPlanIssueDto : actionPlanIssueDtos) {
+ Issue issue = issuesById.get(actionPlanIssueDto.getIssueId());
+ actionPlansByIssue.put(issue, actionPlanIssueDto.toActionPlan());
+ }
+ return actionPlansByIssue;
+ }
+
+ private void setActionPlans(List<Issue> issues, ListMultimap<Issue, ActionPlan> actionPlansByIssueKey){
+ for (Issue issue : issues) {
+ DefaultIssue defaultIssue = (DefaultIssue) issue;
+ List<ActionPlan> actionPlans = actionPlansByIssueKey.get(issue);
+ defaultIssue.setActionPlans(actionPlans);
+ }
+ }
+
public Issue findByKey(String key) {
IssueDto dto = issueDao.selectByKey(key);
return dto != null ? dto.toDefaultIssue() : null;
@@ -172,12 +203,15 @@ public class ServerIssueFinder implements IssueFinder {
private final Paging paging;
private final Map<Issue, Rule> rulesByIssue;
private final Map<Issue, Component> componentsByIssue;
+ private final ListMultimap<Issue, ActionPlan> actionPlansByIssue;
private final boolean securityExclusions;
- DefaultResults(List<Issue> issues, Map<Issue, Rule> rulesByIssue, Map<Issue, Component> componentsByIssue, Paging paging, boolean securityExclusions) {
+ DefaultResults(List<Issue> issues, Map<Issue, Rule> rulesByIssue, Map<Issue, Component> componentsByIssue, ListMultimap<Issue, ActionPlan> actionPlansByIssue,
+ Paging paging, boolean securityExclusions) {
this.issues = issues;
this.rulesByIssue = rulesByIssue;
this.componentsByIssue = componentsByIssue;
+ this.actionPlansByIssue = actionPlansByIssue;
this.paging = paging;
this.securityExclusions = securityExclusions;
}
@@ -203,6 +237,14 @@ public class ServerIssueFinder implements IssueFinder {
return componentsByIssue.values();
}
+ public Collection<ActionPlan> actionPlans(Issue issue) {
+ return actionPlansByIssue.get(issue);
+ }
+
+ public Collection<ActionPlan> actionPlans() {
+ return actionPlansByIssue.values();
+ }
+
public boolean securityExclusions() {
return securityExclusions;
}
diff --git a/sonar-server/src/main/webapp/WEB-INF/db/migrate/394_add_key_to_action_plan.rb b/sonar-server/src/main/webapp/WEB-INF/db/migrate/394_add_key_to_action_plan.rb
new file mode 100644
index 00000000000..ded169f5996
--- /dev/null
+++ b/sonar-server/src/main/webapp/WEB-INF/db/migrate/394_add_key_to_action_plan.rb
@@ -0,0 +1,38 @@
+#
+# Sonar, entreprise quality control tool.
+# Copyright (C) 2008-2013 SonarSource
+# mailto:contact AT sonarsource DOT com
+#
+# SonarQube 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.
+#
+# SonarQube 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.
+#
+
+#
+# Sonar 3.6
+#
+class AddKeyToActionPlan < ActiveRecord::Migration
+
+ class ActionPlan < ActiveRecord::Base
+ end
+
+ def self.up
+ add_column 'action_plans', 'kee', :string, :null => true, :limit => 100
+ ActionPlan.reset_column_information
+ ActionPlan.all.each do |a|
+ a.update_attributes!(:kee => Java::JavaUtil::UUID.randomUUID().toString())
+ end
+ end
+
+end
+
diff --git a/sonar-server/src/test/java/org/sonar/server/issue/ServerIssueFinderTest.java b/sonar-server/src/test/java/org/sonar/server/issue/ServerIssueFinderTest.java
index 4a8b86b9091..d2090f4da40 100644
--- a/sonar-server/src/test/java/org/sonar/server/issue/ServerIssueFinderTest.java
+++ b/sonar-server/src/test/java/org/sonar/server/issue/ServerIssueFinderTest.java
@@ -31,6 +31,8 @@ import org.sonar.api.issue.IssueQuery;
import org.sonar.api.rules.Rule;
import org.sonar.api.web.UserRole;
import org.sonar.core.component.ComponentDto;
+import org.sonar.core.issue.db.ActionPlanIssueDao;
+import org.sonar.core.issue.db.ActionPlanIssueDto;
import org.sonar.core.issue.db.IssueDao;
import org.sonar.core.issue.db.IssueDto;
import org.sonar.core.persistence.MyBatis;
@@ -61,6 +63,7 @@ public class ServerIssueFinderTest {
AuthorizationDao authorizationDao;
DefaultRuleFinder ruleFinder;
ResourceDao resourceDao;
+ ActionPlanIssueDao actionPlanIssueDao;
@Before
public void before() {
@@ -69,7 +72,8 @@ public class ServerIssueFinderTest {
authorizationDao = mock(AuthorizationDao.class);
ruleFinder = mock(DefaultRuleFinder.class);
resourceDao = mock(ResourceDao.class);
- finder = new ServerIssueFinder(mybatis, issueDao, authorizationDao, ruleFinder, resourceDao);
+ actionPlanIssueDao = mock(ActionPlanIssueDao.class);
+ finder = new ServerIssueFinder(mybatis, issueDao, authorizationDao, ruleFinder, resourceDao, actionPlanIssueDao);
}
@Test
@@ -222,7 +226,37 @@ public class ServerIssueFinderTest {
}
@Test
- public void should_get_empty_rule_and_component_from_result_when_no_issue() {
+ public void should_get_action_plans_from_result() {
+ ActionPlanIssueDto actionPlanIssueDto1 = new ActionPlanIssueDto().setIssueId(1L).setKee("A").setName("Short term");
+ ActionPlanIssueDto actionPlanIssueDto2 = new ActionPlanIssueDto().setIssueId(2L).setKee("B").setName("Long term");
+
+ grantAccessRights();
+ IssueQuery issueQuery = mock(IssueQuery.class);
+
+ IssueDto issue1 = new IssueDto().setId(1L).setRuleId(50).setResourceId(123).setKey("ABC")
+ .setComponentKey_unit_test_only("Action.java")
+ .setRuleKey_unit_test_only("squid", "AvoidCycle")
+ .setStatus("OPEN").setResolution("OPEN");
+ IssueDto issue2 = new IssueDto().setId(2L).setRuleId(50).setResourceId(123).setKey("DEF")
+ .setComponentKey_unit_test_only("Action.java")
+ .setRuleKey_unit_test_only("squid", "AvoidCycle")
+ .setStatus("OPEN").setResolution("OPEN");
+ List<IssueDto> dtoList = newArrayList(issue1, issue2);
+ when(issueDao.selectIssueIdsAndComponentsId(eq(issueQuery), any(SqlSession.class))).thenReturn(dtoList);
+ when(issueDao.selectByIds(anyCollection(), any(SqlSession.class))).thenReturn(dtoList);
+
+ when(actionPlanIssueDao.findByIssueIds(anyCollection(), any(SqlSession.class))).thenReturn(newArrayList(actionPlanIssueDto1, actionPlanIssueDto2));
+
+
+ IssueFinder.Results results = finder.find(issueQuery, null, UserRole.USER);
+ assertThat(results.issues()).hasSize(2);
+ Issue issue = results.issues().iterator().next();
+ assertThat(results.issues()).hasSize(2);
+ assertThat(results.actionPlans(issue)).hasSize(1);
+ }
+
+ @Test
+ public void should_get_empty_result_when_no_issue() {
grantAccessRights();
IssueQuery issueQuery = mock(IssueQuery.class);
when(issueDao.selectIssueIdsAndComponentsId(eq(issueQuery), any(SqlSession.class))).thenReturn(Collections.<IssueDto>emptyList());
@@ -233,6 +267,7 @@ public class ServerIssueFinderTest {
assertThat(results.issues()).isEmpty();
assertThat(results.rules()).isEmpty();
assertThat(results.components()).isEmpty();
+ assertThat(results.actionPlans()).isEmpty();
}
private void grantAccessRights() {