]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-3755 refactor comments
authorSimon Brandhof <simon.brandhof@gmail.com>
Thu, 2 May 2013 14:53:25 +0000 (16:53 +0200)
committerSimon Brandhof <simon.brandhof@gmail.com>
Thu, 2 May 2013 16:30:44 +0000 (18:30 +0200)
17 files changed:
sonar-core/src/main/java/org/sonar/core/issue/FieldDiffs.java
sonar-core/src/main/java/org/sonar/core/issue/IssueChange.java [deleted file]
sonar-core/src/main/java/org/sonar/core/issue/IssueComment.java
sonar-core/src/main/java/org/sonar/core/issue/db/ChangeDtoConverter.java
sonar-core/src/main/java/org/sonar/core/issue/db/IssueChangeDao.java
sonar-core/src/main/java/org/sonar/core/issue/db/IssueChangeDto.java
sonar-core/src/main/java/org/sonar/core/issue/db/IssueChangeMapper.java
sonar-core/src/main/java/org/sonar/core/issue/db/IssueStorage.java
sonar-core/src/main/java/org/sonar/core/persistence/DaoUtils.java
sonar-core/src/main/resources/org/sonar/core/issue/db/IssueChangeMapper.xml
sonar-core/src/test/java/org/sonar/core/issue/db/ChangeDtoConverterTest.java
sonar-core/src/test/java/org/sonar/core/issue/db/IssueChangeDaoTest.java
sonar-core/src/test/resources/org/sonar/core/issue/db/IssueChangeDaoTest/shared.xml
sonar-core/src/test/resources/org/sonar/core/issue/db/IssueStorageTest/should_insert_new_issues-result.xml
sonar-server/src/main/java/org/sonar/server/issue/JRubyInternalIssues.java
sonar-server/src/main/java/org/sonar/server/issue/ServerIssueActions.java
sonar-server/src/main/webapp/WEB-INF/app/controllers/api/issues_controller.rb

index a7004285ecdd05ee7061217dda1b8239417dc9d4..f1ee89f083b0d4b6f04dc1413b7d4108457fbe00 100644 (file)
@@ -25,22 +25,53 @@ import com.google.common.collect.Maps;
 
 import javax.annotation.Nullable;
 import java.io.Serializable;
+import java.util.Date;
 import java.util.Map;
 
-public class FieldDiffs extends IssueChange {
+public class FieldDiffs implements Serializable {
 
   public static final Splitter FIELDS_SPLITTER = Splitter.on(',').omitEmptyStrings();
 
+  private String userLogin;
+  private Date createdAt, updatedAt;
   private final Map<String, Diff> diffs = Maps.newLinkedHashMap();
 
-  Map<String, Diff> diffs() {
+  public Map<String, Diff> diffs() {
     return diffs;
   }
 
-  Diff get(String field) {
+  public Diff get(String field) {
     return diffs.get(field);
   }
 
+  public String userLogin() {
+    return userLogin;
+  }
+
+  public FieldDiffs setUserLogin(String s) {
+    this.userLogin = s;
+    return this;
+  }
+
+  public Date createdAt() {
+    return createdAt;
+  }
+
+  public FieldDiffs setCreatedAt(Date d) {
+    this.createdAt = d;
+    return this;
+  }
+
+  public Date updatedAt() {
+    return updatedAt;
+  }
+
+  public FieldDiffs setUpdatedAt(Date d) {
+    this.updatedAt = d;
+    return this;
+  }
+
+
   @SuppressWarnings("unchecked")
   public void setDiff(String field, @Nullable Serializable oldValue, @Nullable Serializable newValue) {
     Diff diff = diffs.get(field);
diff --git a/sonar-core/src/main/java/org/sonar/core/issue/IssueChange.java b/sonar-core/src/main/java/org/sonar/core/issue/IssueChange.java
deleted file mode 100644 (file)
index 527965c..0000000
+++ /dev/null
@@ -1,55 +0,0 @@
-/*
- * 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 java.io.Serializable;
-import java.util.Date;
-
-public abstract class IssueChange implements Serializable {
-  private String userLogin;
-  private Date createdAt, updatedAt;
-
-  public String userLogin() {
-    return userLogin;
-  }
-
-  public IssueChange setUserLogin(String s) {
-    this.userLogin = s;
-    return this;
-  }
-
-  public Date createdAt() {
-    return createdAt;
-  }
-
-  public IssueChange setCreatedAt(Date d) {
-    this.createdAt = d;
-    return this;
-  }
-
-  public Date updatedAt() {
-    return updatedAt;
-  }
-
-  public IssueChange setUpdatedAt(Date d) {
-    this.updatedAt = d;
-    return this;
-  }
-}
index 975c9ae5978f03cf7fa7878d36c6961a74d8b248..a63bdd7de15810457b3afd95721c1d7329be773e 100644 (file)
 package org.sonar.core.issue;
 
 import javax.annotation.Nullable;
+import java.io.Serializable;
 import java.util.Date;
 import java.util.UUID;
 
-public class IssueComment extends IssueChange {
+public class IssueComment implements Serializable {
 
+  private String userLogin;
+  private Date createdAt, updatedAt;
   private String key;
   private String text;
 
@@ -46,6 +49,33 @@ public class IssueComment extends IssueChange {
     return this;
   }
 
+  public String userLogin() {
+    return userLogin;
+  }
+
+  public IssueComment setUserLogin(String userLogin) {
+    this.userLogin = userLogin;
+    return this;
+  }
+
+  public Date createdAt() {
+    return createdAt;
+  }
+
+  public IssueComment setCreatedAt(Date createdAt) {
+    this.createdAt = createdAt;
+    return this;
+  }
+
+  public Date updatedAt() {
+    return updatedAt;
+  }
+
+  public IssueComment setUpdatedAt(Date updatedAt) {
+    this.updatedAt = updatedAt;
+    return this;
+  }
+
   public static IssueComment create(@Nullable String login, String text) {
     IssueComment comment = new IssueComment();
     comment.setKey(UUID.randomUUID().toString());
index 9403cd08d1dd1d56a8131f866fb49f5234ba9712..23015627eb74023a9672acb12e3156ab0dd8ef78 100644 (file)
@@ -22,7 +22,6 @@ package org.sonar.core.issue.db;
 import com.google.common.collect.Lists;
 import org.sonar.core.issue.DefaultIssue;
 import org.sonar.core.issue.FieldDiffs;
-import org.sonar.core.issue.IssueChangeContext;
 import org.sonar.core.issue.IssueComment;
 
 import java.util.Date;
@@ -30,16 +29,16 @@ import java.util.List;
 
 class ChangeDtoConverter {
 
-  static final String TYPE_FIELD_CHANGE = "diff";
+  static final String TYPE_FIELD_CHANGE = "change";
   static final String TYPE_COMMENT = "comment";
 
-  static List<IssueChangeDto> toChangeDtos(DefaultIssue issue) {
+  static List<IssueChangeDto> extractChangeDtos(DefaultIssue issue) {
     List<IssueChangeDto> dtos = Lists.newArrayList();
     for (IssueComment comment : issue.newComments()) {
       dtos.add(commentToDto(issue.key(), comment));
     }
     if (issue.diffs() != null) {
-      dtos.add(diffsToDto(issue.key(), issue.diffs()));
+      dtos.add(changeToDto(issue.key(), issue.diffs()));
     }
     return dtos;
   }
@@ -53,7 +52,7 @@ class ChangeDtoConverter {
     return dto;
   }
 
-  static IssueChangeDto diffsToDto(String issueKey, FieldDiffs diffs) {
+  static IssueChangeDto changeToDto(String issueKey, FieldDiffs diffs) {
     IssueChangeDto dto = newDto(issueKey);
     dto.setChangeType(TYPE_FIELD_CHANGE);
     dto.setChangeData(diffs.toString());
@@ -71,4 +70,21 @@ class ChangeDtoConverter {
     dto.setUpdatedAt(new Date());
     return dto;
   }
+
+  public static IssueComment dtoToComment(IssueChangeDto dto) {
+    return new IssueComment()
+      .setText(dto.getChangeData())
+      .setKey(dto.getKey())
+      .setCreatedAt(dto.getCreatedAt())
+      .setUpdatedAt(dto.getUpdatedAt())
+      .setUserLogin(dto.getUserLogin());
+  }
+
+  public static FieldDiffs dtoToChange(IssueChangeDto dto) {
+    FieldDiffs diffs = FieldDiffs.parse(dto.getChangeData());
+    diffs.setUserLogin(dto.getUserLogin());
+    diffs.setCreatedAt(dto.getCreatedAt());
+    diffs.setUpdatedAt(dto.getUpdatedAt());
+    return diffs;
+  }
 }
index 42a213d02e800eac65a1e10f65688dbdc1a15ea1..2399ccd642525974d5863223e638c932db382d30 100644 (file)
@@ -23,9 +23,10 @@ package org.sonar.core.issue.db;
 import org.apache.ibatis.session.SqlSession;
 import org.sonar.api.BatchComponent;
 import org.sonar.api.ServerComponent;
+import org.sonar.core.issue.FieldDiffs;
+import org.sonar.core.issue.IssueComment;
 import org.sonar.core.persistence.MyBatis;
 
-import javax.annotation.CheckForNull;
 import java.util.List;
 
 /**
@@ -39,28 +40,34 @@ public class IssueChangeDao implements BatchComponent, ServerComponent {
     this.mybatis = mybatis;
   }
 
-  @CheckForNull
-  public IssueChangeDto selectById(long id) {
-    SqlSession session = mybatis.openSession();
-    try {
-      IssueChangeMapper mapper = session.getMapper(IssueChangeMapper.class);
-      return mapper.selectById(id);
-    } finally {
-      MyBatis.closeQuietly(session);
+  public IssueComment[] selectIssueComments(String issueKey) {
+    List<IssueChangeDto> dtos = selectByIssue(issueKey, ChangeDtoConverter.TYPE_COMMENT);
+    IssueComment[] result = new IssueComment[dtos.size()];
+    for (int index = 0; index < dtos.size(); index++) {
+      result[index] = ChangeDtoConverter.dtoToComment(dtos.get(index));
     }
+    return result;
+  }
+
+  public FieldDiffs[] selectIssueChanges(String issueKey) {
+    List<IssueChangeDto> dtos = selectByIssue(issueKey, ChangeDtoConverter.TYPE_FIELD_CHANGE);
+    FieldDiffs[] result = new FieldDiffs[dtos.size()];
+    for (int index = 0; index < dtos.size(); index++) {
+      result[index] = ChangeDtoConverter.dtoToChange(dtos.get(index));
+    }
+    return result;
   }
 
   /**
    * Issue changes ordered by descending creation date.
    */
-  public List<IssueChangeDto> selectByIssue(String issueKey) {
+  private List<IssueChangeDto> selectByIssue(String issueKey, String changeType) {
     SqlSession session = mybatis.openSession();
     try {
       IssueChangeMapper mapper = session.getMapper(IssueChangeMapper.class);
-      return mapper.selectByIssue(issueKey);
+      return mapper.selectByIssueAndType(issueKey, changeType);
     } finally {
       MyBatis.closeQuietly(session);
     }
   }
-
 }
index 1ab40848704c0a16e1daaa608ded739e3f771d7e..389f7c697ad97817ef38ebe9a020201e7df2d968 100644 (file)
@@ -31,7 +31,7 @@ import java.util.Date;
 public final class IssueChangeDto {
 
   private Long id;
-  private String key;
+  private String kee;
   private String issueKey;
   private String userLogin;
   private String changeType;
@@ -49,11 +49,11 @@ public final class IssueChangeDto {
   }
 
   public String getKey() {
-    return key;
+    return kee;
   }
 
   public IssueChangeDto setKey(String key) {
-    this.key = key;
+    this.kee = key;
     return this;
   }
 
index 077d1f37f40cf11f9cb21b06861ae7b6f63105db..7f54bc0899b24b3d8e05fa2f1b8e247907039ac1 100644 (file)
@@ -20,6 +20,8 @@
 
 package org.sonar.core.issue.db;
 
+import org.apache.ibatis.annotations.Param;
+
 import java.util.List;
 
 /**
@@ -29,11 +31,9 @@ public interface IssueChangeMapper {
 
   void insert(IssueChangeDto dto);
 
-  IssueChangeDto selectById(long id);
-
   /**
    * Issue changes ordered by descending creation date.
    */
-  List<IssueChangeDto> selectByIssue(String issueKey);
+  List<IssueChangeDto> selectByIssueAndType(@Param("issueKey") String issueKey, @Param("changeType") String changeType);
 
 }
index 25872741a9fb7fe19f5264f4b89f8c905e407d98..34bcec939d8c8d3a2bad666ca871dfeb3ec78c65 100644 (file)
@@ -67,7 +67,7 @@ public abstract class IssueStorage {
             conflicts.add(issue);
           }
         }
-        for (IssueChangeDto changeDto : ChangeDtoConverter.toChangeDtos(issue)) {
+        for (IssueChangeDto changeDto : ChangeDtoConverter.extractChangeDtos(issue)) {
           session.insert(MYBATIS_INSERT_CHANGE, changeDto);
         }
       }
index 8c97b86b38c4402c541c83b8afd65815650723a2..dbb79e2a5830a2762ad24fda678f88aff0a9adaf 100644 (file)
@@ -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.IssueChangeDao;
 import org.sonar.core.issue.db.IssueDao;
 import org.sonar.core.measure.MeasureFilterDao;
 import org.sonar.core.properties.PropertiesDao;
@@ -57,6 +58,7 @@ public final class DaoUtils {
       DuplicationDao.class,
       GraphDao.class,
       IssueDao.class,
+      IssueChangeDao.class,
       LoadedTemplateDao.class,
       MeasureFilterDao.class,
       PropertiesDao.class,
index 3dd61b273e16e8d4cfefd58412d085e1c0e140ca..5ca7dd30001b54e135f551c0f725245721b36c4b 100644 (file)
 <mapper namespace="org.sonar.core.issue.db.IssueChangeMapper">
 
   <sql id="issueChangeColumns">
-    i.id,
-    i.kee as kee,
-    i.issue_key as issueKey,
-    i.user_login as userLogin,
-    i.change_type as changeType,
-    i.change_data as changeData,
-    i.created_at as createdAt,
-    i.updated_at as updatedAt
+    c.id,
+    c.kee as kee,
+    c.issue_key as issueKey,
+    c.user_login as userLogin,
+    c.change_type as changeType,
+    c.change_data as changeData,
+    c.created_at as createdAt,
+    c.updated_at as updatedAt
   </sql>
 
   <insert id="insert" parameterType="IssueChange" useGeneratedKeys="true" keyProperty="id">
     VALUES (#{id}, #{key}, #{issueKey}, #{userLogin}, #{changeType}, #{changeData}, #{createdAt}, #{updatedAt})
   </insert>
 
-  <select id="selectById" parameterType="long" resultType="IssueChange">
+  <select id="selectByIssueAndType" parameterType="map" resultType="IssueChange">
     select
     <include refid="issueChangeColumns"/>
-    from issue_changes i
-    where i.id=#{id}
-  </select>
-
-  <select id="selectByIssue" parameterType="string" resultType="IssueChange">
-    select
-    <include refid="issueChangeColumns"/>
-    from issue_changes i
-    where i.issue_key=#{issueKey}
-    order by i.created_at desc
+    from issue_changes c
+    where c.issue_key=#{issueKey} and c.change_type=#{changeType}
+    order by c.created_at desc
   </select>
 
 </mapper>
index dec00ee6cf9e6cc5fc090d91bc37e147494474c1..28721482da7d2312fe3bc5caf2deaed35ba77925 100644 (file)
@@ -51,10 +51,10 @@ public class ChangeDtoConverterTest {
     diffs.setDiff("severity", "INFO", "BLOCKER");
     diffs.setUserLogin("emmerik");
 
-    IssueChangeDto dto = ChangeDtoConverter.diffsToDto("ABCDE", diffs);
+    IssueChangeDto dto = ChangeDtoConverter.changeToDto("ABCDE", diffs);
 
     assertThat(dto.getChangeData()).isEqualTo("severity=INFO|BLOCKER");
-    assertThat(dto.getChangeType()).isEqualTo("diff");
+    assertThat(dto.getChangeType()).isEqualTo("change");
     assertThat(dto.getCreatedAt()).isNotNull();
     assertThat(dto.getUpdatedAt()).isNotNull();
     assertThat(dto.getIssueKey()).isEqualTo("ABCDE");
@@ -69,10 +69,10 @@ public class ChangeDtoConverterTest {
     issue.setFieldDiff(context, "severity", "INFO", "BLOCKER");
     issue.addComment(comment);
 
-    List<IssueChangeDto> changeDtos = ChangeDtoConverter.toChangeDtos(issue);
+    List<IssueChangeDto> changeDtos = ChangeDtoConverter.extractChangeDtos(issue);
     assertThat(changeDtos).hasSize(2);
     assertThat(changeDtos.get(0).getChangeType()).isEqualTo("comment");
-    assertThat(changeDtos.get(1).getChangeType()).isEqualTo("diff");
+    assertThat(changeDtos.get(1).getChangeType()).isEqualTo("change");
 
   }
 }
index c3c23a7c4a1a841ed9dd91bfdd25e60cf84e3ec9..a777e227e79781164d865c9f9e4026ad17cf525b 100644 (file)
@@ -21,16 +21,15 @@ package org.sonar.core.issue.db;
 
 import org.junit.Before;
 import org.junit.Test;
+import org.sonar.core.issue.FieldDiffs;
+import org.sonar.core.issue.IssueComment;
 import org.sonar.core.persistence.AbstractDaoTestCase;
 
-import java.util.Date;
-import java.util.List;
-
 import static org.fest.assertions.Assertions.assertThat;
 
 public class IssueChangeDaoTest extends AbstractDaoTestCase {
 
-  private IssueChangeDao dao;
+  IssueChangeDao dao;
 
   @Before
   public void createDao() {
@@ -38,27 +37,28 @@ public class IssueChangeDaoTest extends AbstractDaoTestCase {
   }
 
   @Test
-  public void should_select_by_id() {
+  public void should_select_issue_comments() {
     setupData("shared");
 
-    IssueChangeDto dto = dao.selectById(100L);
-    assertThat(dto.getId()).isEqualTo(100L);
-    assertThat(dto.getIssueKey()).isEqualTo("1000");
-    assertThat(dto.getUserLogin()).isEqualTo("arthur");
-    assertThat(dto.getChangeType()).isEqualTo("comment");
-    assertThat(dto.getChangeData()).isEqualTo("this is a comment");
-    assertThat(dto.getCreatedAt()).isNotNull();
-    assertThat(dto.getUpdatedAt()).isNotNull();
+    IssueComment[] comments = dao.selectIssueComments("1000");
+    assertThat(comments).hasSize(2);
+    IssueComment first = comments[0];
+    assertThat(first.text()).isEqualTo("recent comment");
+    assertThat(first.userLogin()).isEqualTo("arthur");
+    assertThat(first.key()).isEqualTo("FGHIJ");
+
+    IssueComment second = comments[1];
+    assertThat(second.text()).isEqualTo("old comment");
   }
 
   @Test
-  public void should_select_by_issue() {
+  public void should_select_issue_changes() {
     setupData("shared");
 
-    List<IssueChangeDto> ordered = dao.selectByIssue("1000");
-    assertThat(ordered).hasSize(2);
-    assertThat(ordered.get(0).getId()).isEqualTo(101);
-    assertThat(ordered.get(1).getId()).isEqualTo(100);
+    FieldDiffs[] ordered = dao.selectIssueChanges("1000");
+    assertThat(ordered).hasSize(1);
+    FieldDiffs.Diff severityDiff = ordered[0].get("severity");
+    assertThat(severityDiff.oldValue()).isEqualTo("MAJOR");
+    assertThat(severityDiff.newValue()).isEqualTo("BLOCKER");
   }
-
 }
index 739f6c802df52634b6f66c8c47f7295935f5c040..e9d0022f88914d210f39071b1200c2ce8b418314 100644 (file)
@@ -2,22 +2,34 @@
 
   <issue_changes
       id="100"
+      kee="ABCDE"
       issue_key="1000"
       user_login="arthur"
       change_type="comment"
-      change_data="this is a comment"
+      change_data="old comment"
       created_at="2013-01-01"
       updated_at="2013-01-01"
       />
 
   <issue_changes
       id="101"
+      kee="[null]"
       issue_key="1000"
       user_login="arthur"
-      change_type="fields"
-      change_data="severity:MAJOR,BLOCKER"
+      change_type="change"
+      change_data="severity=MAJOR|BLOCKER"
       created_at="2013-02-02"
       updated_at="2013-02-02"
       />
 
+  <issue_changes
+      id="102"
+      kee="FGHIJ"
+      issue_key="1000"
+      user_login="arthur"
+      change_type="comment"
+      change_data="recent comment"
+      created_at="2013-05-05"
+      updated_at="2013-05-05"
+      />
 </dataset>
index 2414004833615d67615e260c5c2c806d6bd88503..c0861b3e3059ccc2f2da8fc5f79b99944c9b4957 100644 (file)
@@ -6,5 +6,5 @@
       attributes="foo=bar" />
 
   <issue_changes id="1" kee="FGHIJ" issue_key="ABCDE" change_type="comment" user_login="emmerik" change_data="the comment" created_at="[null]" updated_at="[null]" />
-  <issue_changes id="2" kee="[null]" issue_key="ABCDE" change_type="diff" user_login="emmerik" change_data="severity=INFO|BLOCKER" created_at="[null]" updated_at="[null]" />
+  <issue_changes id="2" kee="[null]" issue_key="ABCDE" change_type="change" user_login="emmerik" change_data="severity=INFO|BLOCKER" created_at="[null]" updated_at="[null]" />
 </dataset>
\ No newline at end of file
index 32092c2139bf8377d04f5daec1c920823e0ca62d..2dc008f801f556d350eec559cd8ab41ddf498afc 100644 (file)
@@ -24,6 +24,7 @@ import org.sonar.api.issue.Issue;
 import org.sonar.api.rule.RuleKey;
 import org.sonar.core.issue.DefaultIssue;
 import org.sonar.core.issue.DefaultIssueBuilder;
+import org.sonar.core.issue.FieldDiffs;
 import org.sonar.core.issue.IssueComment;
 import org.sonar.core.issue.workflow.Transition;
 import org.sonar.server.platform.UserSession;
@@ -58,14 +59,18 @@ public class JRubyInternalIssues implements ServerComponent {
     return actions.setSeverity(issueKey, severity, UserSession.get());
   }
 
-  public DefaultIssue addComment(String issueKey, String comment) {
+  public IssueComment addComment(String issueKey, String comment) {
     return actions.addComment(issueKey, comment, UserSession.get());
   }
 
-  public List<IssueComment> comments(String issueKey) {
+  public IssueComment[] comments(String issueKey) {
     return actions.comments(issueKey, UserSession.get());
   }
 
+  public FieldDiffs[] changes(String issueKey) {
+    return actions.changes(issueKey, UserSession.get());
+  }
+
   public Issue create(Map<String, String> parameters) {
     String componentKey = parameters.get("component");
     // TODO verify authorization
index 0c2d125c38fb86000b1ec7725885b6f7e96c33b7..56438a7fbd3ac73b28777898dfe91de7d2b80135 100644 (file)
@@ -22,10 +22,8 @@ package org.sonar.server.issue;
 import org.sonar.api.ServerComponent;
 import org.sonar.api.issue.Issue;
 import org.sonar.api.web.UserRole;
-import org.sonar.core.issue.DefaultIssue;
-import org.sonar.core.issue.IssueChangeContext;
-import org.sonar.core.issue.IssueComment;
-import org.sonar.core.issue.IssueUpdater;
+import org.sonar.core.issue.*;
+import org.sonar.core.issue.db.IssueChangeDao;
 import org.sonar.core.issue.db.IssueDao;
 import org.sonar.core.issue.db.IssueDto;
 import org.sonar.core.issue.db.IssueStorage;
@@ -48,6 +46,7 @@ public class ServerIssueActions implements ServerComponent {
   private final IssueWorkflow workflow;
   private final IssueUpdater issueUpdater;
   private final IssueDao issueDao;
+  private final IssueChangeDao issueChangeDao;
   private final IssueStorage issueStorage;
   private final AuthorizationDao authorizationDao;
 
@@ -55,12 +54,13 @@ public class ServerIssueActions implements ServerComponent {
                             IssueDao issueDao,
                             IssueStorage issueStorage,
                             AuthorizationDao authorizationDao,
-                            IssueUpdater issueUpdater) {
+                            IssueUpdater issueUpdater, IssueChangeDao issueChangeDao) {
     this.workflow = workflow;
     this.issueDao = issueDao;
     this.issueStorage = issueStorage;
     this.issueUpdater = issueUpdater;
     this.authorizationDao = authorizationDao;
+    this.issueChangeDao = issueChangeDao;
   }
 
   public List<Transition> listTransitions(String issueKey, UserSession userSession) {
@@ -105,17 +105,23 @@ public class ServerIssueActions implements ServerComponent {
     return issue;
   }
 
-  public DefaultIssue addComment(String issueKey, String comment, UserSession userSession) {
+  public IssueComment addComment(String issueKey, String comment, UserSession userSession) {
     DefaultIssue issue = loadIssue(issueKey, userSession);
 
     IssueChangeContext context = IssueChangeContext.createUser(new Date(), userSession.login());
     issueUpdater.addComment(issue, comment, context);
     issueStorage.save(issue);
-    return issue;
+    return issue.newComments().get(0);
+  }
+
+  public IssueComment[] comments(String issueKey, UserSession userSession) {
+    // TODO verify authorization
+    return issueChangeDao.selectIssueComments(issueKey);
   }
 
-  public List<IssueComment> comments(String issueKey, UserSession userSession) {
-    throw new UnsupportedOperationException("TODO");
+  public FieldDiffs[] changes(String issueKey, UserSession userSession) {
+    // TODO verify authorization
+    return issueChangeDao.selectIssueChanges(issueKey);
   }
 
   public Issue create(DefaultIssue issue, UserSession userSession) {
index 9f89bc7e9a9f58701fc82678687bccca2c4e9d20..a259731a5fbae018d29fd719f2732f1f04091ebb 100644 (file)
@@ -29,11 +29,11 @@ class Api::IssuesController < Api::ApiController
   def search
     results = Api.issues.find(params)
     render :json => jsonp(
-      {
-        :securityExclusions => results.securityExclusions,
-        :paging => paging_to_json(results.paging),
-        :issues => results.issues.map { |issue| issue_to_json(issue) }
-      }
+        {
+            :securityExclusions => results.securityExclusions,
+            :paging => paging_to_json(results.paging),
+            :issues => results.issues.map { |issue| issue_to_json(issue) }
+        }
     )
   end
 
@@ -49,9 +49,9 @@ class Api::IssuesController < Api::ApiController
     issue_key = params[:issue]
     transitions = Internal.issues.listTransitions(issue_key)
     render :json => jsonp(
-      {
-        :transitions => transitions.map { |t| t.key() }
-      }
+        {
+            :transitions => transitions.map { |t| t.key() }
+        }
     )
   end
 
@@ -68,7 +68,7 @@ class Api::IssuesController < Api::ApiController
     issue = Internal.issues.doTransition(params[:issue], params[:transition])
     if issue
       render :json => jsonp({
-                              :issue => issue_to_json(issue)
+                                :issue => issue_to_json(issue)
                             })
     else
       render :status => 400
@@ -90,8 +90,25 @@ class Api::IssuesController < Api::ApiController
     require_parameters :issue, :text
 
     text = Api::Utils.read_post_request_param(params[:text])
-    issue = Internal.issues.addComment(params[:issue], text)
-    render :json => jsonp({:comment => comment_to_json(issue.newComments().get(0))})
+    comment = Internal.issues.addComment(params[:issue], text)
+    render :json => jsonp({:comment => comment_to_json(comment)})
+  end
+
+  #
+  # GET /api/issues/comments?issue=<key>
+  #
+  # -- Example
+  # curl -v -u admin:admin 'http://localhost:9000/api/issues/comments?issue=4a2881e7-825e-4140-a154-01f420c43d11'
+  #
+  def comments
+    require_parameters :issue
+
+    comments = Internal.issues.comments(params[:issue])
+    render :json => jsonp(
+        {
+            :comments => comments.map { |comment| comment_to_json(comment) }
+        }
+    )
   end
 
   #
@@ -160,11 +177,11 @@ class Api::IssuesController < Api::ApiController
 
   def issue_to_json(issue)
     json = {
-      :key => issue.key,
-      :component => issue.componentKey,
-      :rule => issue.ruleKey.toString(),
-      :resolution => issue.resolution,
-      :status => issue.status
+        :key => issue.key,
+        :component => issue.componentKey,
+        :rule => issue.ruleKey.toString(),
+        :resolution => issue.resolution,
+        :status => issue.status
     }
     json[:severity] = issue.severity if issue.severity
     json[:desc] = issue.description if issue.description
@@ -182,19 +199,19 @@ class Api::IssuesController < Api::ApiController
 
   def comment_to_json(comment)
     {
-      :key => comment.key(),
-      :text => comment.text(),
-      :createdAt => format_java_datetime(comment.createdAt()),
-      :login => comment.userLogin()
+        :key => comment.key(),
+        :text => comment.text(),
+        :createdAt => format_java_datetime(comment.createdAt()),
+        :login => comment.userLogin()
     }
   end
 
   def paging_to_json(paging)
     {
-      :pageIndex => paging.pageIndex,
-      :pageSize => paging.pageSize,
-      :total => paging.total,
-      :pages => paging.pages
+        :pageIndex => paging.pageIndex,
+        :pageSize => paging.pageSize,
+        :total => paging.total,
+        :pages => paging.pages
     }
   end