]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-4301 Add project Id on issues table
authorJulien Lancelot <julien.lancelot@gmail.com>
Wed, 22 May 2013 07:42:17 +0000 (09:42 +0200)
committerJulien Lancelot <julien.lancelot@gmail.com>
Wed, 22 May 2013 07:42:17 +0000 (09:42 +0200)
56 files changed:
sonar-batch/src/main/java/org/sonar/batch/issue/ScanIssueStorage.java
sonar-batch/src/test/java/org/sonar/batch/issue/ScanIssueStorageTest.java
sonar-batch/src/test/resources/org/sonar/batch/issue/ScanIssueStorageTest/should_fail_if_unknown_component.xml [deleted file]
sonar-batch/src/test/resources/org/sonar/batch/issue/ScanIssueStorageTest/should_fail_to_load_component_id_if_unknown_component.xml [new file with mode: 0644]
sonar-core/src/main/java/org/sonar/core/issue/DefaultIssue.java
sonar-core/src/main/java/org/sonar/core/issue/DefaultIssueBuilder.java
sonar-core/src/main/java/org/sonar/core/issue/db/IssueDto.java
sonar-core/src/main/java/org/sonar/core/issue/db/IssueStorage.java
sonar-core/src/main/java/org/sonar/core/persistence/DatabaseVersion.java
sonar-core/src/main/resources/org/sonar/core/issue/db/IssueMapper.xml
sonar-core/src/main/resources/org/sonar/core/persistence/rows-h2.sql
sonar-core/src/main/resources/org/sonar/core/persistence/schema-h2.ddl
sonar-core/src/test/java/org/sonar/core/issue/db/IssueDaoTest.java
sonar-core/src/test/java/org/sonar/core/issue/db/IssueDtoTest.java
sonar-core/src/test/java/org/sonar/core/issue/db/IssueMapperTest.java
sonar-core/src/test/java/org/sonar/core/issue/db/IssueStorageTest.java
sonar-core/src/test/resources/org/sonar/core/issue/db/IssueDaoTest/should_select_all.xml
sonar-core/src/test/resources/org/sonar/core/issue/db/IssueDaoTest/should_select_by_action_plans.xml
sonar-core/src/test/resources/org/sonar/core/issue/db/IssueDaoTest/should_select_by_assigned.xml
sonar-core/src/test/resources/org/sonar/core/issue/db/IssueDaoTest/should_select_by_component_root.xml
sonar-core/src/test/resources/org/sonar/core/issue/db/IssueDaoTest/should_select_by_date_creation.xml
sonar-core/src/test/resources/org/sonar/core/issue/db/IssueDaoTest/should_select_by_ids.xml
sonar-core/src/test/resources/org/sonar/core/issue/db/IssueDaoTest/should_select_by_key.xml
sonar-core/src/test/resources/org/sonar/core/issue/db/IssueDaoTest/should_select_by_planned.xml
sonar-core/src/test/resources/org/sonar/core/issue/db/IssueDaoTest/should_select_by_query.xml
sonar-core/src/test/resources/org/sonar/core/issue/db/IssueDaoTest/should_select_by_resolved.xml
sonar-core/src/test/resources/org/sonar/core/issue/db/IssueDaoTest/should_select_by_rules.xml
sonar-core/src/test/resources/org/sonar/core/issue/db/IssueDaoTest/should_select_issue_and_component_ids.xml
sonar-core/src/test/resources/org/sonar/core/issue/db/IssueDaoTest/should_select_issue_and_project_ids.xml
sonar-core/src/test/resources/org/sonar/core/issue/db/IssueDaoTest/should_select_open_issues.xml
sonar-core/src/test/resources/org/sonar/core/issue/db/IssueDaoTest/should_select_returned_sorted_result_by_assignee.xml
sonar-core/src/test/resources/org/sonar/core/issue/db/IssueDaoTest/should_select_returned_sorted_result_by_close_date.xml
sonar-core/src/test/resources/org/sonar/core/issue/db/IssueDaoTest/should_select_returned_sorted_result_by_creation_date.xml
sonar-core/src/test/resources/org/sonar/core/issue/db/IssueDaoTest/should_select_returned_sorted_result_by_severity.xml
sonar-core/src/test/resources/org/sonar/core/issue/db/IssueDaoTest/should_select_returned_sorted_result_by_status.xml
sonar-core/src/test/resources/org/sonar/core/issue/db/IssueDaoTest/should_select_returned_sorted_result_by_update_date.xml
sonar-core/src/test/resources/org/sonar/core/issue/db/IssueMapperTest/testInsert-result.xml
sonar-core/src/test/resources/org/sonar/core/issue/db/IssueMapperTest/testUpdate-result.xml
sonar-core/src/test/resources/org/sonar/core/issue/db/IssueMapperTest/testUpdate.xml
sonar-core/src/test/resources/org/sonar/core/issue/db/IssueStorageTest/should_insert_new_issues-result.xml
sonar-core/src/test/resources/org/sonar/core/issue/db/IssueStorageTest/should_update_issues-result.xml
sonar-core/src/test/resources/org/sonar/core/issue/db/IssueStorageTest/should_update_issues.xml
sonar-server/src/main/java/org/sonar/server/issue/ServerIssueStorage.java
sonar-server/src/main/webapp/WEB-INF/db/migrate/395_create_issues.rb [deleted file]
sonar-server/src/main/webapp/WEB-INF/db/migrate/396_create_issue_changes.rb [deleted file]
sonar-server/src/main/webapp/WEB-INF/db/migrate/398_create_issues.rb [new file with mode: 0644]
sonar-server/src/main/webapp/WEB-INF/db/migrate/398_delete_review_duplications.rb [deleted file]
sonar-server/src/main/webapp/WEB-INF/db/migrate/399_create_issue_changes.rb [new file with mode: 0644]
sonar-server/src/main/webapp/WEB-INF/db/migrate/399_migrate_violations_to_issues.rb [deleted file]
sonar-server/src/main/webapp/WEB-INF/db/migrate/400_delete_review_duplications.rb [new file with mode: 0644]
sonar-server/src/main/webapp/WEB-INF/db/migrate/401_migrate_violations_to_issues.rb [new file with mode: 0644]
sonar-server/src/test/java/org/sonar/server/issue/ServerIssueStorageTest.java [new file with mode: 0644]
sonar-server/src/test/resources/org/sonar/server/issue/ServerIssueStorageTest/should_fail_to_load_component_id_if_unknown_component.xml [new file with mode: 0644]
sonar-server/src/test/resources/org/sonar/server/issue/ServerIssueStorageTest/should_fail_to_load_project_id_if_unknown_component.xml [new file with mode: 0644]
sonar-server/src/test/resources/org/sonar/server/issue/ServerIssueStorageTest/should_load_component_id_from_db.xml [new file with mode: 0644]
sonar-server/src/test/resources/org/sonar/server/issue/ServerIssueStorageTest/should_load_project_id_from_db.xml [new file with mode: 0644]

index 124d5f2137b5d49bcb110c65f3bbb29522908ae7..13f12576c5b5c9eb99ecebb18d441d013afc77ba 100644 (file)
@@ -43,7 +43,7 @@ public class ScanIssueStorage extends IssueStorage implements BatchComponent {
 
   @Override
   protected int componentId(DefaultIssue issue) {
-    Snapshot snapshot = snapshotCache.get(issue.componentKey());
+    Snapshot snapshot = getSnapshot(issue);
     if (snapshot != null) {
       return snapshot.getResourceId();
     }
@@ -55,4 +55,22 @@ public class ScanIssueStorage extends IssueStorage implements BatchComponent {
     }
     return resourceDto.getId().intValue();
   }
+
+  @Override
+  protected int projectId(DefaultIssue issue) {
+    Snapshot snapshot = getSnapshot(issue);
+    if (snapshot != null) {
+      return snapshot.getRootProjectId();
+    }
+    throw new IllegalStateException("Unknown component: " + issue.componentKey());
+  }
+
+  private Snapshot getSnapshot(DefaultIssue issue) {
+    Snapshot snapshot = snapshotCache.get(issue.componentKey());
+    if (snapshot != null) {
+      return snapshot;
+    }
+    return null;
+  }
+
 }
index e805a06b68c2430040844ec54891adef5be6ebc4..8bb35616a75d10530d44b343af63d184d1be3a1f 100644 (file)
@@ -62,8 +62,8 @@ public class ScanIssueStorageTest extends AbstractDaoTestCase {
   }
 
   @Test
-  public void should_fail_if_unknown_component() throws Exception {
-    setupData("should_fail_if_unknown_component");
+  public void should_fail_to_load_component_id_if_unknown_component() throws Exception {
+    setupData("should_fail_to_load_component_id_if_unknown_component");
     SnapshotCache snapshotCache = mock(SnapshotCache.class);
     when(snapshotCache.get("struts:Action.java")).thenReturn(null);
 
@@ -76,6 +76,31 @@ public class ScanIssueStorageTest extends AbstractDaoTestCase {
     }
   }
 
+  @Test
+  public void should_load_project_id() throws Exception {
+    SnapshotCache snapshotCache = mock(SnapshotCache.class);
+    when(snapshotCache.get("struts:Action.java")).thenReturn(new Snapshot().setResourceId(123).setRootProjectId(100));
+
+    ScanIssueStorage storage = new ScanIssueStorage(getMyBatis(), new FakeRuleFinder(), snapshotCache, new ResourceDao(getMyBatis()));
+    int projectId = storage.projectId(new DefaultIssue().setComponentKey("struts:Action.java"));
+
+    assertThat(projectId).isEqualTo(100);
+  }
+
+  @Test
+  public void should_fail_to_load_project_id_if_unknown_component() throws Exception {
+    SnapshotCache snapshotCache = mock(SnapshotCache.class);
+    when(snapshotCache.get("struts:Action.java")).thenReturn(null);
+
+    ScanIssueStorage storage = new ScanIssueStorage(getMyBatis(), new FakeRuleFinder(), snapshotCache, new ResourceDao(getMyBatis()));
+    try {
+      storage.projectId(new DefaultIssue().setComponentKey("struts:Action.java"));
+      fail();
+    } catch (Exception e) {
+      assertThat(e).hasMessage("Unknown component: struts:Action.java");
+    }
+  }
+
   static class FakeRuleFinder implements RuleFinder {
 
     @Override
@@ -90,7 +115,7 @@ public class ScanIssueStorageTest extends AbstractDaoTestCase {
 
     @Override
     public Rule findByKey(RuleKey key) {
-      Rule rule = new Rule().setRepositoryKey(key.repository()).setKey(key.rule());
+      Rule rule = Rule.create().setRepositoryKey(key.repository()).setKey(key.rule());
       rule.setId(200);
       return rule;
     }
diff --git a/sonar-batch/src/test/resources/org/sonar/batch/issue/ScanIssueStorageTest/should_fail_if_unknown_component.xml b/sonar-batch/src/test/resources/org/sonar/batch/issue/ScanIssueStorageTest/should_fail_if_unknown_component.xml
deleted file mode 100644 (file)
index 5ed00ba..0000000
+++ /dev/null
@@ -1 +0,0 @@
-<dataset></dataset>
\ No newline at end of file
diff --git a/sonar-batch/src/test/resources/org/sonar/batch/issue/ScanIssueStorageTest/should_fail_to_load_component_id_if_unknown_component.xml b/sonar-batch/src/test/resources/org/sonar/batch/issue/ScanIssueStorageTest/should_fail_to_load_component_id_if_unknown_component.xml
new file mode 100644 (file)
index 0000000..5ed00ba
--- /dev/null
@@ -0,0 +1 @@
+<dataset></dataset>
\ No newline at end of file
index 5f2ba2f22f89864e6e9cf524a094b9bf88858ef4..a020f13445b7f5060eee053d240e9731b1df1b79 100644 (file)
@@ -35,6 +35,7 @@ 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;
@@ -45,6 +46,7 @@ public class DefaultIssue implements Issue {
 
   private String key;
   private String componentKey;
+  private String projectKey;
   private RuleKey ruleKey;
   private String severity;
   private boolean manualSeverity = false;
@@ -97,6 +99,15 @@ public class DefaultIssue implements Issue {
     return this;
   }
 
+  public String projectKey() {
+    return projectKey;
+  }
+
+  public DefaultIssue setProjectKey(String projectKey) {
+    this.projectKey = projectKey;
+    return this;
+  }
+
   public RuleKey ruleKey() {
     return ruleKey;
   }
index 88847d97804fa39dfd7af8139185d5ed590d8cfe..0546585f7aa1708b74b305b72082001340c6c3c0 100644 (file)
@@ -29,6 +29,7 @@ import org.sonar.api.rule.RuleKey;
 import org.sonar.api.rule.Severity;
 
 import javax.annotation.Nullable;
+
 import java.util.Date;
 import java.util.Map;
 import java.util.UUID;
@@ -36,6 +37,7 @@ import java.util.UUID;
 public class DefaultIssueBuilder implements Issuable.IssueBuilder {
 
   private String componentKey;
+  private String projectKey;
   private RuleKey ruleKey;
   private Integer line;
   private String message;
@@ -54,6 +56,11 @@ public class DefaultIssueBuilder implements Issuable.IssueBuilder {
     return this;
   }
 
+  public DefaultIssueBuilder projectKey(String projectKey) {
+    this.projectKey = projectKey;
+    return this;
+  }
+
   public DefaultIssueBuilder createdDate(@Nullable Date date) {
     this.createdDate = date;
     return this;
@@ -107,6 +114,8 @@ public class DefaultIssueBuilder implements Issuable.IssueBuilder {
   @Override
   public DefaultIssue build() {
     Preconditions.checkNotNull(componentKey, "Component key must be set");
+    // TODO
+    //Preconditions.checkNotNull(projectKey, "Project key must be set");
     Preconditions.checkNotNull(ruleKey, "Rule key must be set");
 
     DefaultIssue issue = new DefaultIssue();
@@ -119,6 +128,7 @@ public class DefaultIssueBuilder implements Issuable.IssueBuilder {
     issue.setCreationDate(date);
     issue.setUpdateDate(date);
     issue.setComponentKey(componentKey);
+    issue.setProjectKey(projectKey);
     issue.setRuleKey(ruleKey);
     issue.setMessage(message);
     issue.setSeverity(Objects.firstNonNull(severity, Severity.MAJOR));
index c79b4837172d8522395b5d6cdd0b419921e79efe..d6179b68b8290da1504e53620a089bf9bc177d92 100644 (file)
@@ -29,6 +29,7 @@ import org.sonar.core.issue.DefaultIssue;
 
 import javax.annotation.CheckForNull;
 import javax.annotation.Nullable;
+
 import java.util.Date;
 
 /**
@@ -39,6 +40,7 @@ public final class IssueDto {
   private Long id;
   private String kee;
   private Integer resourceId;
+  private Integer projectId;
   private Integer ruleId;
   private String severity;
   private boolean manualSeverity;
@@ -67,6 +69,7 @@ public final class IssueDto {
   private transient String ruleKey;
   private transient String ruleRepo;
   private transient String componentKey;
+  private transient String projectKey;
 
   public Long getId() {
     return id;
@@ -95,6 +98,15 @@ public final class IssueDto {
     return this;
   }
 
+  public Integer getProjectId() {
+    return projectId;
+  }
+
+  public IssueDto setProjectId(Integer projectId) {
+    this.projectId = projectId;
+    return this;
+  }
+
   public Integer getRuleId() {
     return ruleId;
   }
@@ -288,6 +300,10 @@ public final class IssueDto {
     return componentKey;
   }
 
+  public String getProjectKey() {
+    return projectKey;
+  }
+
   /**
    * Only for unit tests
    */
@@ -305,12 +321,20 @@ public final class IssueDto {
     return this;
   }
 
+  /**
+   * Only for unit tests
+   */
+  public IssueDto setProjectKey_unit_test_only(String projectKey) {
+    this.projectKey = projectKey;
+    return this;
+  }
+
   @Override
   public String toString() {
     return ToStringBuilder.reflectionToString(this, ToStringStyle.SHORT_PREFIX_STYLE);
   }
 
-  public static IssueDto toDtoForInsert(DefaultIssue issue, Integer componentId, Integer ruleId, Date now) {
+  public static IssueDto toDtoForInsert(DefaultIssue issue, Integer componentId, Integer projectId, Integer ruleId, Date now) {
     return new IssueDto()
       .setKee(issue.key())
       .setLine(issue.line())
@@ -325,6 +349,7 @@ public final class IssueDto {
       .setAssignee(issue.assignee())
       .setRuleId(ruleId)
       .setResourceId(componentId)
+      .setProjectId(projectId)
       .setActionPlanKey(issue.actionPlanKey())
       .setIssueAttributes(issue.attributes() != null ? KeyValueFormat.format(issue.attributes()) : "")
       .setAuthorLogin(issue.authorLogin())
@@ -372,6 +397,7 @@ public final class IssueDto {
     issue.setAssignee(assignee);
     issue.setAttributes(KeyValueFormat.parse(Objects.firstNonNull(issueAttributes, "")));
     issue.setComponentKey(componentKey);
+    issue.setProjectKey(projectKey);
     issue.setManualSeverity(manualSeverity);
     issue.setRuleKey(RuleKey.of(ruleRepo, ruleKey));
     issue.setActionPlanKey(actionPlanKey);
index e38c4198eecf29adec579f27f7e4c48ccf293ecc..f89b426f73fb392bfad92ea289aa25fc00b5b214 100644 (file)
@@ -58,8 +58,9 @@ public abstract class IssueStorage {
       for (DefaultIssue issue : issues) {
         if (issue.isNew()) {
           int componentId = componentId(issue);
+          int projectId = projectId(issue);
           int ruleId = ruleId(issue);
-          IssueDto dto = IssueDto.toDtoForInsert(issue, componentId, ruleId, now);
+          IssueDto dto = IssueDto.toDtoForInsert(issue, componentId, projectId, ruleId, now);
           issueMapper.insert(dto);
 
         } else if (issue.isChanged()) {
@@ -96,6 +97,8 @@ public abstract class IssueStorage {
 
   protected abstract int componentId(DefaultIssue issue);
 
+  protected abstract int projectId(DefaultIssue issue);
+
   private int ruleId(Issue issue) {
     Rule rule = ruleFinder.findByKey(issue.ruleKey());
     if (rule == null) {
index 39fdd04cd9867d0e002b5f4efedc714f4639676d..3ff9b2c536fc876554174da5122d222a40f2d286 100644 (file)
@@ -32,7 +32,7 @@ import java.util.List;
  */
 public class DatabaseVersion implements BatchComponent, ServerComponent {
 
-  public static final int LAST_VERSION = 399;
+  public static final int LAST_VERSION = 401;
 
   public static enum Status {
     UP_TO_DATE, REQUIRES_UPGRADE, REQUIRES_DOWNGRADE, FRESH_INSTALL
index 3023ba9b7285cd6fa06c551a2f2e9d5ea73adf99..4b27aa1f2ddf444c43089e2a1d9fab2045787407 100644 (file)
@@ -8,6 +8,7 @@
     i.id,
     i.kee as kee,
     i.resource_id as resourceId,
+    i.project_id as projectId,
     i.rule_id as ruleId,
     i.action_plan_key as actionPlanKey,
     i.severity as severity,
@@ -29,7 +30,8 @@
     i.updated_at as updatedAt,
     r.plugin_rule_key as ruleKey,
     r.plugin_name as ruleRepo,
-    p.kee as componentKey
+    p.kee as componentKey,
+    root.kee as projectKey
   </sql>
 
   <sql id="sortFilter">
   </sql>
 
   <insert id="insert" parameterType="Issue" useGeneratedKeys="false" keyProperty="id">
-    INSERT INTO issues (kee, resource_id, rule_id, action_plan_key, severity, manual_severity,
+    INSERT INTO issues (kee, resource_id, project_id, rule_id, action_plan_key, severity, manual_severity,
     message, line, effort_to_fix, status,
     resolution, checksum, reporter, assignee, author_login, issue_attributes, issue_creation_date, issue_update_date,
     issue_close_date, created_at, updated_at)
-    VALUES (#{kee}, #{resourceId}, #{ruleId}, #{actionPlanKey}, #{severity}, #{manualSeverity},
+    VALUES (#{kee}, #{resourceId}, #{projectId}, #{ruleId}, #{actionPlanKey}, #{severity}, #{manualSeverity},
     #{message}, #{line}, #{effortToFix}, #{status},
     #{resolution}, #{checksum}, #{reporter}, #{assignee}, #{authorLogin}, #{issueAttributes}, #{issueCreationDate},
     #{issueUpdateDate}, #{issueCloseDate}, #{createdAt}, #{updatedAt})
     <selectKey order="BEFORE" resultType="Long" keyProperty="id">
       select issues_seq.NEXTVAL from DUAL
     </selectKey>
-    INSERT INTO issues (id, kee, resource_id, rule_id, action_plan_key, severity, manual_severity,
+    INSERT INTO issues (id, kee, resource_id, project_id, rule_id, action_plan_key, severity, manual_severity,
     message, line, effort_to_fix, status,
     resolution, checksum, reporter, assignee, author_login, issue_attributes, issue_creation_date, issue_update_date,
     issue_close_date, created_at, updated_at)
-    VALUES (#{id}, #{kee}, #{resourceId}, #{ruleId}, #{actionPlanKey}, #{severity}, #{manualSeverity},
+    VALUES (#{id}, #{kee}, #{resourceId}, #{projectId}, #{ruleId}, #{actionPlanKey}, #{severity}, #{manualSeverity},
     #{message}, #{line}, #{effortToFix}, #{status},
     #{resolution}, #{checksum}, #{reporter}, #{assignee}, #{authorLogin}, #{issueAttributes}, #{issueCreationDate},
     #{issueUpdateDate}, #{issueCloseDate}, #{createdAt}, #{updatedAt})
   <select id="selectByKey" parameterType="String" resultType="Issue">
     select
     <include refid="issueColumns"/>
-    from issues i, rules r, projects p
-    where i.kee=#{kee} and i.rule_id=r.id and p.id=i.resource_id
+    from issues i, rules r, projects p, projects root
+    where i.kee=#{kee} and i.rule_id=r.id and p.id=i.resource_id and i.project_id=root.id
   </select>
 
   <select id="selectNonClosedIssues" parameterType="int" resultType="Issue">
     select distinct
     <include refid="issueColumns"/>
-    from issues i, rules r, projects p
+    from issues i, rules r, projects p, projects root
     where i.status &lt;&gt; 'CLOSED'
     and (p.root_id=#{id} or (p.root_id is null and p.id=#{id}))
     and i.resource_id=p.id
+    and i.project_id=root.id
     and r.id=i.rule_id
   </select>
 
   <select id="selectByIds" parameterType="map" resultType="Issue">
     select
     <include refid="issueColumns"/>
-    from issues i, rules r, projects p
+    from issues i, rules r, projects p, projects root
     <where>
       and
       <foreach collection="ids" open="i.id in (" close=")" item="list" separator=") or i.id in (">
       </foreach>
       and i.rule_id=r.id
       and p.id=i.resource_id
+      and i.project_id=root.id
     </where>
     <include refid="sortFilter"/>
   </select>
   </select>
 
   <sql id="selectQueryConditions">
-    from issues i, projects p, rules r
+    from issues i, projects p, rules r, projects root
     <if test="componentRoots != null and componentRoots.size() > 0">
       , projects rootprojects, snapshots rootsnapshots, snapshots s
     </if>
     </if>
     <where>
       and i.resource_id=p.id
+      and i.project_id=root.id
       and i.rule_id=r.id
       <if test="componentRoots != null and componentRoots.size() > 0">
         and rootprojects.enabled=${_true}
index a6aaa812b52fda2a7942cd5e1453fb6d34e02d31..59ecbe41c2dc859ba2f2b8d141bc94fd0a5710ce 100644 (file)
@@ -159,11 +159,11 @@ INSERT INTO SCHEMA_MIGRATIONS(VERSION) VALUES ('388');
 INSERT INTO SCHEMA_MIGRATIONS(VERSION) VALUES ('391');
 INSERT INTO SCHEMA_MIGRATIONS(VERSION) VALUES ('392');
 INSERT INTO SCHEMA_MIGRATIONS(VERSION) VALUES ('394');
-INSERT INTO SCHEMA_MIGRATIONS(VERSION) VALUES ('395');
-INSERT INTO SCHEMA_MIGRATIONS(VERSION) VALUES ('396');
 INSERT INTO SCHEMA_MIGRATIONS(VERSION) VALUES ('397');
 INSERT INTO SCHEMA_MIGRATIONS(VERSION) VALUES ('398');
 INSERT INTO SCHEMA_MIGRATIONS(VERSION) VALUES ('399');
+INSERT INTO SCHEMA_MIGRATIONS(VERSION) VALUES ('400');
+INSERT INTO SCHEMA_MIGRATIONS(VERSION) VALUES ('401');
 
 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;
index 79018bd010c437ca754426abba3afb8d2639171e..8f5fb36dc4abe532b72fbee6aa44f5602ba9f460 100644 (file)
@@ -520,6 +520,7 @@ CREATE TABLE "ISSUES" (
   "ID" INTEGER NOT NULL GENERATED BY DEFAULT AS IDENTITY (START WITH 1, INCREMENT BY 1),
   "KEE" VARCHAR(50) UNIQUE NOT NULL,
   "RESOURCE_ID" INTEGER NOT NULL,
+  "PROJECT_ID" INTEGER,
   "RULE_ID" INTEGER,
   "SEVERITY" VARCHAR(10),
   "MANUAL_SEVERITY" BOOLEAN NOT NULL,
@@ -678,6 +679,8 @@ CREATE UNIQUE INDEX "ISSUES_KEE" ON "ISSUES" ("KEE");
 
 CREATE INDEX "ISSUES_RESOURCE_ID" ON "ISSUES" ("RESOURCE_ID");
 
+CREATE INDEX "ISSUES_PROJECT_ID" ON "ISSUES" ("PROJECT_ID");
+
 CREATE INDEX "ISSUES_RULE_ID" ON "ISSUES" ("RULE_ID");
 
 CREATE INDEX "ISSUES_SEVERITY" ON "ISSUES" ("SEVERITY");
index 8ed5fdc238407a1a3e3cb05b77b77f85cf675e87..6f8d460f0c148bd3875bb8cfc77be3d1edff7b4e 100644 (file)
@@ -50,6 +50,7 @@ public class IssueDaoTest extends AbstractDaoTestCase {
     assertThat(issue.getKee()).isEqualTo("ABCDE");
     assertThat(issue.getId()).isEqualTo(100L);
     assertThat(issue.getResourceId()).isEqualTo(401);
+    assertThat(issue.getProjectId()).isEqualTo(399);
     assertThat(issue.getRuleId()).isEqualTo(500);
     assertThat(issue.getSeverity()).isEqualTo("BLOCKER");
     assertThat(issue.isManualSeverity()).isFalse();
@@ -71,6 +72,7 @@ public class IssueDaoTest extends AbstractDaoTestCase {
     assertThat(issue.getRuleRepo()).isEqualTo("squid");
     assertThat(issue.getRule()).isEqualTo("AvoidCycle");
     assertThat(issue.getComponentKey()).isEqualTo("Action.java");
+    assertThat(issue.getProjectKey()).isEqualTo("struts");
   }
 
   @Test
@@ -94,6 +96,7 @@ public class IssueDaoTest extends AbstractDaoTestCase {
     assertThat(issue.getRuleRepo()).isEqualTo("squid");
     assertThat(issue.getRule()).isEqualTo("AvoidCycle");
     assertThat(issue.getComponentKey()).isEqualTo("Action.java");
+    assertThat(issue.getProjectKey()).isEqualTo("struts");
   }
 
   @Test
index a935fbcdcbdb39f522fc5d3b6fc97c66e74c174a..339473eb0505a7a386f0cd350b704e77a0811990 100644 (file)
@@ -57,8 +57,10 @@ public class IssueDtoTest {
         .setKee("100")
         .setRuleId(1)
         .setRuleKey_unit_test_only("squid", "AvoidCycle")
-        .setComponentKey_unit_test_only("component-key")
+        .setComponentKey_unit_test_only("org.sonar.sample:Sample")
+        .setProjectKey_unit_test_only("org.sonar.sample")
         .setResourceId(1)
+        .setProjectId(1)
         .setStatus(Issue.STATUS_CLOSED)
         .setResolution(Issue.RESOLUTION_FALSE_POSITIVE)
         .setEffortToFix(15.0)
@@ -77,7 +79,8 @@ public class IssueDtoTest {
     DefaultIssue issue = dto.toDefaultIssue();
     assertThat(issue.key()).isEqualTo("100");
     assertThat(issue.ruleKey().toString()).isEqualTo("squid:AvoidCycle");
-    assertThat(issue.componentKey()).isEqualTo("component-key");
+    assertThat(issue.componentKey()).isEqualTo("org.sonar.sample:Sample");
+    assertThat(issue.projectKey()).isEqualTo("org.sonar.sample");
     assertThat(issue.status()).isEqualTo(Issue.STATUS_CLOSED);
     assertThat(issue.resolution()).isEqualTo(Issue.RESOLUTION_FALSE_POSITIVE);
     assertThat(issue.effortToFix()).isEqualTo(15.0);
index 942514f541193d1e47c2e04a515a072d556e477d..f6844202bf37f6af4dfff26200a0d38f75c8a33e 100644 (file)
@@ -40,6 +40,7 @@ public class IssueMapperTest extends AbstractDaoTestCase {
   public void testInsert() throws Exception {
     IssueDto dto = new IssueDto();
     dto.setResourceId(123);
+    dto.setProjectId(100);
     dto.setRuleId(200);
     dto.setKee("ABCDE");
     dto.setLine(500);
@@ -73,6 +74,7 @@ public class IssueMapperTest extends AbstractDaoTestCase {
 
     IssueDto dto = new IssueDto();
     dto.setResourceId(123);
+    dto.setProjectId(100);
     dto.setRuleId(200);
     dto.setKee("ABCDE");
     dto.setLine(500);
index fd7f8f58b1e5119cd0260b8104c670a85c533add..47cd904bff171d68b0fcc6980be9c9700ad72243 100644 (file)
@@ -118,6 +118,11 @@ public class IssueStorageTest extends AbstractDaoTestCase {
     protected int componentId(DefaultIssue issue) {
       return 100;
     }
+
+    @Override
+    protected int projectId(DefaultIssue issue) {
+      return 10;
+    }
   }
 
   static class FakeRuleFinder implements RuleFinder {
index 1536bfcd739006c8f9e445a48c95c9757e615684..dc88f9291bf147cea73c678ceb826bf11184a288 100644 (file)
@@ -5,6 +5,7 @@
       id="100"
       kee="ABCDE-1"
       resource_id="401"
+      project_id="399"
       rule_id="500"
       severity="BLOCKER"
       manual_severity="[false]"
@@ -29,6 +30,7 @@
       id="101"
       kee="ABCDE-2"
       resource_id="401"
+      project_id="399"
       rule_id="500"
       severity="BLOCKER"
       manual_severity="[false]"
@@ -55,6 +57,7 @@
       id="102"
       kee="ABCDE-3"
       resource_id="401"
+      project_id="399"
       rule_id="501"
       severity="BLOCKER"
       manual_severity="[false]"
index ca8d6ea634fb4f8d454205dde8dc98edddbd4aac..6c3452b6e1795585c49f21d07a1b7493e2fc2d70 100644 (file)
@@ -4,6 +4,7 @@
       id="100"
       kee="ABCDE-1"
       resource_id="401"
+      project_id="399"
       rule_id="500"
       action_plan_key="ABC"
       severity="BLOCKER"
@@ -29,6 +30,7 @@
       id="101"
       kee="ABCDE-2"
       resource_id="401"
+      project_id="399"
       rule_id="500"
       action_plan_key="ABC"
       severity="BLOCKER"
@@ -55,6 +57,7 @@
       id="102"
       kee="ABCDE-3"
       resource_id="401"
+      project_id="399"
       rule_id="501"
       action_plan_key="DEF"
       severity="BLOCKER"
index e0ab0887a437b2b388d155ef2122f600656f6470..8663285f41c6e1331896a424b5ac24eb71667c18 100644 (file)
@@ -5,6 +5,7 @@
       id="100"
       kee="ABCDE-1"
       resource_id="401"
+      project_id="399"
       rule_id="500"
       severity="BLOCKER"
       manual_severity="[false]"
@@ -29,6 +30,7 @@
       id="101"
       kee="ABCDE-2"
       resource_id="401"
+      project_id="399"
       rule_id="500"
       severity="BLOCKER"
       manual_severity="[false]"
@@ -55,6 +57,7 @@
       id="102"
       kee="ABCDE-3"
       resource_id="401"
+      project_id="399"
       rule_id="501"
       severity="BLOCKER"
       manual_severity="[false]"
index 72522e5536047c0e058aa41137fd82bc9d2dd53d..ee35e68f6b1a3501e83beee62e02acb811fc4261 100644 (file)
@@ -4,6 +4,7 @@
       id="100"
       kee="100"
       resource_id="401"
+      project_id="399"
       rule_id="500"
       severity="BLOCKER"
       manual_severity="[false]"
@@ -28,6 +29,7 @@
       id="101"
       kee="101"
       resource_id="402"
+      project_id="399"
       rule_id="501"
       severity="MAJOR"
       manual_severity="[false]"
index be5857897227d9cb07c15a01ffa0d316e2c60e62..ed7532a06664ac681e341cfb25faad18bcdce260 100644 (file)
@@ -4,6 +4,7 @@
       id="100"
       kee="ABCDE-1"
       resource_id="401"
+      project_id="399"
       rule_id="500"
       severity="BLOCKER"
       manual_severity="[false]"
@@ -28,6 +29,7 @@
       id="101"
       kee="ABCDE-2"
       resource_id="401"
+      project_id="399"
       rule_id="500"
       severity="BLOCKER"
       manual_severity="[false]"
index 986c069e9e43030dda5237baf2c4708581f1c3c5..33530589537089eaff994f01cc74681aa31d5806 100644 (file)
@@ -5,6 +5,7 @@
       id="100"
       kee="ABCDE-1"
       resource_id="401"
+      project_id="399"
       rule_id="500"
       severity="BLOCKER"
       manual_severity="[false]"
@@ -29,6 +30,7 @@
       id="101"
       kee="ABCDE-2"
       resource_id="401"
+      project_id="399"
       rule_id="500"
       severity="BLOCKER"
       manual_severity="[false]"
@@ -55,6 +57,7 @@
       id="102"
       kee="ABCDE"
       resource_id="401"
+      project_id="399"
       rule_id="501"
       severity="BLOCKER"
       manual_severity="[false]"
index 853f580260f4102764c93d513bcdb8f19d7bd248..705a0cb90fc4df8e75bfc1dfd969aa0a5e11e83b 100644 (file)
@@ -4,6 +4,7 @@
       id="100"
       kee="ABCDE"
       resource_id="401"
+      project_id="399"
       rule_id="500"
       severity="BLOCKER"
       manual_severity="[false]"
index 8ab824158e0bcfcc52d388c12efb56155b787d69..58f34c08ccf33662895789b2f9de2260856539e0 100644 (file)
@@ -4,6 +4,7 @@
       id="100"
       kee="ABCDE"
       resource_id="401"
+      project_id="399"
       rule_id="500"
       action_plan_key="ABC"
       severity="BLOCKER"
@@ -29,6 +30,7 @@
       id="101"
       kee="ABCDF"
       resource_id="401"
+      project_id="399"
       rule_id="500"
       action_plan_key="ABC"
       severity="BLOCKER"
@@ -55,6 +57,7 @@
       id="102"
       kee="ABCDG"
       resource_id="401"
+      project_id="399"
       rule_id="501"
       action_plan_key="[null]"
       severity="BLOCKER"
index 8c8853780618e1509689533ac4288a172a2e7908..cb897cd64ca6420c3131c48bb73cfea112a31a56 100644 (file)
@@ -4,6 +4,7 @@
       id="100"
       kee="ABCDE"
       resource_id="401"
+      project_id="399"
       rule_id="500"
       severity="BLOCKER"
       manual_severity="[false]"
index aa6105074d77b47d8660e26b00d6337d373dac38..2134f045362671b3c2b452344527e696d8900f08 100644 (file)
@@ -4,6 +4,7 @@
       id="100"
       kee="ABCDE-1"
       resource_id="401"
+      project_id="399"
       rule_id="500"
       severity="BLOCKER"
       manual_severity="[false]"
@@ -28,6 +29,7 @@
       id="101"
       kee="ABCDE-2"
       resource_id="401"
+      project_id="399"
       rule_id="500"
       severity="BLOCKER"
       manual_severity="[false]"
@@ -52,6 +54,7 @@
       id="102"
       kee="ABCDE-3"
       resource_id="401"
+      project_id="399"
       rule_id="501"
       severity="BLOCKER"
       manual_severity="[false]"
index 746d55ec02144d8497f0c5a1b4f20b4fcd44dcb3..24f222c2fa6e6c3aef6c677d30b67381eab0d950 100644 (file)
@@ -5,6 +5,7 @@
       id="100"
       kee="ABCDE-1"
       resource_id="401"
+      project_id="399"
       rule_id="500"
       severity="BLOCKER"
       manual_severity="[false]"
@@ -29,6 +30,7 @@
       id="101"
       kee="ABCDE-2"
       resource_id="401"
+      project_id="399"
       rule_id="500"
       severity="BLOCKER"
       manual_severity="[false]"
@@ -55,6 +57,7 @@
       id="102"
       kee="ABCDE-3"
       resource_id="401"
+      project_id="399"
       rule_id="501"
       severity="BLOCKER"
       manual_severity="[false]"
index 1536bfcd739006c8f9e445a48c95c9757e615684..dc88f9291bf147cea73c678ceb826bf11184a288 100644 (file)
@@ -5,6 +5,7 @@
       id="100"
       kee="ABCDE-1"
       resource_id="401"
+      project_id="399"
       rule_id="500"
       severity="BLOCKER"
       manual_severity="[false]"
@@ -29,6 +30,7 @@
       id="101"
       kee="ABCDE-2"
       resource_id="401"
+      project_id="399"
       rule_id="500"
       severity="BLOCKER"
       manual_severity="[false]"
@@ -55,6 +57,7 @@
       id="102"
       kee="ABCDE-3"
       resource_id="401"
+      project_id="399"
       rule_id="501"
       severity="BLOCKER"
       manual_severity="[false]"
index 23853c9aa950c47875061de7f352752a8459a1f9..b581467050c6101af371bbbe22c8fc06b4e666e5 100644 (file)
@@ -5,6 +5,7 @@
       id="100"
       kee="ABCDE-1"
       resource_id="401"
+      project_id="399"
       rule_id="500"
       severity="BLOCKER"
       manual_severity="[false]"
@@ -29,6 +30,7 @@
       id="101"
       kee="ABCDE-2"
       resource_id="401"
+      project_id="399"
       rule_id="500"
       severity="BLOCKER"
       manual_severity="[false]"
@@ -55,6 +57,7 @@
       id="102"
       kee="ABCDE-3"
       resource_id="401"
+      project_id="399"
       rule_id="501"
       severity="BLOCKER"
       manual_severity="[false]"
index 2314297dc66f9b4aabf348c5255d7216ecd186ba..a7be9512891e4294b0b65f685aef7a84000897f6 100644 (file)
@@ -4,6 +4,7 @@
       id="100"
       kee="100"
       resource_id="401"
+      project_id="399"
       rule_id="500"
       severity="BLOCKER"
       manual_severity="[false]"
@@ -28,6 +29,7 @@
       id="101"
       kee="101"
       resource_id="402"
+      project_id="399"
       rule_id="501"
       severity="MAJOR"
       manual_severity="[false]"
@@ -52,6 +54,7 @@
       id="102"
       kee="102"
       resource_id="402"
+      project_id="399"
       rule_id="501"
       severity="MAJOR"
       manual_severity="[false]"
index 2e5e66e3bbb912195a4f2ee302a41f852090f77b..ffebd21b94d137955da72a90a36f1d04e7afe9ef 100644 (file)
@@ -5,6 +5,7 @@
       id="100"
       kee="ABCDE-1"
       resource_id="401"
+      project_id="399"
       rule_id="500"
       severity="BLOCKER"
       manual_severity="[false]"
@@ -29,6 +30,7 @@
       id="101"
       kee="ABCDE-2"
       resource_id="401"
+      project_id="399"
       rule_id="500"
       severity="BLOCKER"
       manual_severity="[false]"
@@ -55,6 +57,7 @@
       id="102"
       kee="ABCDE-3"
       resource_id="401"
+      project_id="399"
       rule_id="501"
       severity="BLOCKER"
       manual_severity="[false]"
index 4de78ef752e869699e848a82ec3c678ac527ce42..34af29003852c229816a51ef37f564f00f8f173a 100644 (file)
@@ -5,6 +5,7 @@
       id="100"
       kee="ABCDE-1"
       resource_id="401"
+      project_id="399"
       rule_id="500"
       severity="BLOCKER"
       manual_severity="[false]"
@@ -29,6 +30,7 @@
       id="101"
       kee="ABCDE-2"
       resource_id="401"
+      project_id="399"
       rule_id="500"
       severity="BLOCKER"
       manual_severity="[false]"
@@ -55,6 +57,7 @@
       id="102"
       kee="ABCDE-3"
       resource_id="401"
+      project_id="399"
       rule_id="501"
       severity="BLOCKER"
       manual_severity="[false]"
index 4af4496b6ee6ce63f6d830a7472b258da2cf7f2e..0662d409941a3881f35154aec477d3914b90a3ab 100644 (file)
@@ -4,6 +4,7 @@
       id="100"
       kee="ABCDE-1"
       resource_id="401"
+      project_id="399"
       rule_id="500"
       severity="BLOCKER"
       manual_severity="[false]"
@@ -28,6 +29,7 @@
       id="101"
       kee="ABCDE-2"
       resource_id="401"
+      project_id="399"
       rule_id="500"
       severity="BLOCKER"
       manual_severity="[false]"
@@ -52,6 +54,7 @@
       id="102"
       kee="ABCDE-3"
       resource_id="401"
+      project_id="399"
       rule_id="501"
       severity="BLOCKER"
       manual_severity="[false]"
index ae7bf8136591754aca84c53c16d4efe2033d9c70..c86cba753bdf7fd499282ca02391e023bf3b8c7d 100644 (file)
@@ -4,6 +4,7 @@
       id="100"
       kee="ABCDE-1"
       resource_id="401"
+      project_id="399"
       rule_id="500"
       severity="MINOR"
       manual_severity="[false]"
@@ -28,6 +29,7 @@
       id="101"
       kee="ABCDE-2"
       resource_id="401"
+      project_id="399"
       rule_id="500"
       severity="BLOCKER"
       manual_severity="[false]"
@@ -52,6 +54,7 @@
       id="102"
       kee="ABCDE-3"
       resource_id="401"
+      project_id="399"
       rule_id="501"
       severity="MAJOR"
       manual_severity="[false]"
index 963dfcfa98dfe0afc00e15bb08b5647be6623be1..8c81f058bc27b04b6b116f58dfe2f0138c836f1a 100644 (file)
@@ -5,6 +5,7 @@
       id="100"
       kee="ABCDE-1"
       resource_id="401"
+      project_id="399"
       rule_id="500"
       severity="BLOCKER"
       manual_severity="[false]"
@@ -29,6 +30,7 @@
       id="101"
       kee="ABCDE-2"
       resource_id="401"
+      project_id="399"
       rule_id="500"
       severity="BLOCKER"
       manual_severity="[false]"
@@ -55,6 +57,7 @@
       id="102"
       kee="ABCDE-3"
       resource_id="401"
+      project_id="399"
       rule_id="501"
       severity="BLOCKER"
       manual_severity="[false]"
index 0fe273586250c8142acca34649a68e0f9bc8cb6f..32dab2f33714a0e7aebd5d3df2c9883b1c960295 100644 (file)
@@ -4,6 +4,7 @@
       id="100"
       kee="ABCDE-1"
       resource_id="401"
+      project_id="399"
       rule_id="500"
       severity="BLOCKER"
       manual_severity="[false]"
@@ -28,6 +29,7 @@
       id="101"
       kee="ABCDE-2"
       resource_id="401"
+      project_id="399"
       rule_id="500"
       severity="BLOCKER"
       manual_severity="[false]"
@@ -52,6 +54,7 @@
       id="102"
       kee="ABCDE-3"
       resource_id="401"
+      project_id="399"
       rule_id="501"
       severity="BLOCKER"
       manual_severity="[false]"
index 9bf0949ff96ed990eed6cd7900a20210fca265dd..bed990591239547075bd3e50396556df57d2fb6b 100644 (file)
@@ -3,6 +3,7 @@
       id="100"
       kee="ABCDE"
       resource_id="123"
+      project_id="100"
       rule_id="200"
       severity="BLOCKER"
       manual_severity="[false]"
index 7b426decddedb071c6d3d43bf109c8d8ad1c7d66..ee9bb753acd605f2bf64804029fae3651ad8912d 100644 (file)
@@ -1,27 +1,9 @@
-<!--
-  ~ 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.
-  -->
 <dataset>
   <issues
       id="100"
       kee="ABCDE"
       resource_id="123"
+      project_id="100"
       rule_id="200"
       severity="BLOCKER"
       manual_severity="[false]"
index f6f06e40abda696fe6b9fc3087c821ea9ef9c652..9d200a67a21f7514702bde87c70a9e74edf74394 100644 (file)
@@ -1,27 +1,9 @@
-<!--
-  ~ 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.
-  -->
 <dataset>
   <issues
       id="100"
       kee="ABCDE"
       resource_id="123"
+      project_id="100"
       rule_id="200"
       severity="INFO"
       manual_severity="[false]"
index aa41626b8dfcbdf932dcb05e3cc7ff4508c862c0..226874e1bec4c238c3447effa1e09b7193934356 100644 (file)
@@ -7,6 +7,7 @@
       message="[null]"
       line="5000"
       resource_id="100"
+      project_id="10"
       rule_id="200"
       created_at="[null]"
       updated_at="[null]"
index d7a35807753b16fc683985372906d09e52dc7e47..2a03f31263adac2326d6104a7ae86ebd9adc2d3a 100644 (file)
@@ -12,6 +12,7 @@
           message="[null]"
           line="5000"
           resource_id="100"
+          project_id="10"
           rule_id="200"
           created_at="2013-05-18"
           updated_at="2013-05-18"
index d3460496f19d43f5cc211b33a504e9ed3f26e317..8ff383e64544bc0bd50611f40d627e1847d51983 100644 (file)
@@ -12,6 +12,7 @@
           message="[null]"
           line="3000"
           resource_id="100"
+          project_id="10"
           rule_id="200"
           created_at="2010-01-01"
           updated_at="2011-02-02"
index 31258a65253696628eaae353b064e799b6bff1eb..035cb4aae9e42a469e6ca2eb1816f0a07cdee495 100644 (file)
@@ -45,4 +45,13 @@ public class ServerIssueStorage extends IssueStorage implements ServerComponent
     }
     return resourceDto.getId().intValue();
   }
+
+  @Override
+  protected int projectId(DefaultIssue issue) {
+    ResourceDto resourceDto = resourceDao.getRootProjectByComponentKey(issue.componentKey());
+    if (resourceDto == null) {
+      throw new IllegalStateException("Unknown component: " + issue.componentKey());
+    }
+    return resourceDto.getId().intValue();
+  }
 }
diff --git a/sonar-server/src/main/webapp/WEB-INF/db/migrate/395_create_issues.rb b/sonar-server/src/main/webapp/WEB-INF/db/migrate/395_create_issues.rb
deleted file mode 100644 (file)
index febc742..0000000
+++ /dev/null
@@ -1,66 +0,0 @@
-#
-# 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 CreateIssues < ActiveRecord::Migration
-
-  def self.up
-    create_table :issues do |t|
-      t.column :kee,                  :string,    :null => false,   :limit => 50
-      t.column :resource_id,          :integer,   :null => false
-      t.column :rule_id,              :integer,   :null => true
-      t.column :severity,                                        :string,        :null => true,          :limit => 10
-      t.column :manual_severity,      :boolean,   :null => false
-      t.column :message,              :string,    :null => true,    :limit => 4000
-      t.column :line,                 :integer,   :null => true
-      t.column :effort_to_fix,        :decimal,   :null => true,    :precision => 30,   :scale => 20
-      t.column :status,               :string ,   :null => true,    :limit => 20
-      t.column :resolution,           :string ,   :null => true,    :limit => 20
-      t.column :checksum,             :string ,   :null => true,    :limit => 1000
-      t.column :reporter,             :string,    :null => true,         :limit => 40
-      t.column :assignee,             :string,    :null => true,         :limit => 40
-      t.column :author_login,         :string,    :null => true,    :limit => 100
-      t.column :action_plan_key,      :string,    :null => true,    :limit => 50
-      t.column :issue_attributes,           :string,    :null => true,    :limit => 4000
-
-      # functional dates
-      t.column :issue_creation_date,  :datetime,  :null => true
-      t.column :issue_close_date,     :datetime,  :null => true
-      t.column :issue_update_date,    :datetime,  :null => true
-
-      # technical dates
-      t.column :created_at,           :datetime,  :null => true
-      t.column :updated_at,           :datetime,  :null => true
-    end
-
-    add_index :issues,  :kee,                 :name => 'issues_kee',         :unique => true
-    add_index :issues,  :resource_id,         :name => 'issues_resource_id'
-    add_index :issues,  :rule_id,             :name => 'issues_rule_id'
-    add_index :issues,  :severity,            :name => 'issues_severity'
-    add_index :issues,  :status,              :name => 'issues_status'
-    add_index :issues,  :assignee,            :name => 'issues_assignee'
-    add_index :issues,  :action_plan_key,     :name => 'issues_action_plan_key'
-    add_index :issues,  :issue_creation_date, :name => 'issues_creation_date'
-  end
-
-end
-
diff --git a/sonar-server/src/main/webapp/WEB-INF/db/migrate/396_create_issue_changes.rb b/sonar-server/src/main/webapp/WEB-INF/db/migrate/396_create_issue_changes.rb
deleted file mode 100644 (file)
index 016380c..0000000
+++ /dev/null
@@ -1,42 +0,0 @@
-#
-# 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 CreateIssueChanges < ActiveRecord::Migration
-
-  def self.up
-    create_table :issue_changes do |t|
-      t.column :kee,                :string,    :null => true,    :limit => 50
-      t.column :issue_key,          :string,    :null => false,   :limit => 50
-      t.column :user_login,         :string,    :null => true,   :limit => 40
-      t.column :change_type,                           :string,          :null => true,          :limit => 20
-      t.column :change_data,        :text,      :null => true
-      t.column :created_at,         :datetime,  :null => true
-      t.column :updated_at,         :datetime,  :null => true
-    end
-
-    add_index :issue_changes,  :kee,         :name => 'issue_changes_kee'
-    add_index :issue_changes,  :issue_key,   :name => 'issue_changes_issue_key'
-  end
-
-end
-
diff --git a/sonar-server/src/main/webapp/WEB-INF/db/migrate/398_create_issues.rb b/sonar-server/src/main/webapp/WEB-INF/db/migrate/398_create_issues.rb
new file mode 100644 (file)
index 0000000..548fada
--- /dev/null
@@ -0,0 +1,68 @@
+#
+# 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 CreateIssues < ActiveRecord::Migration
+
+  def self.up
+    create_table :issues do |t|
+      t.column :kee,                  :string,    :null => false,   :limit => 50
+      t.column :resource_id,          :integer,   :null => false
+      t.column :project_id,           :integer,   :null => true
+      t.column :rule_id,              :integer,   :null => true
+      t.column :severity,                                        :string,        :null => true,          :limit => 10
+      t.column :manual_severity,      :boolean,   :null => false
+      t.column :message,              :string,    :null => true,    :limit => 4000
+      t.column :line,                 :integer,   :null => true
+      t.column :effort_to_fix,        :decimal,   :null => true,    :precision => 30,   :scale => 20
+      t.column :status,               :string ,   :null => true,    :limit => 20
+      t.column :resolution,           :string ,   :null => true,    :limit => 20
+      t.column :checksum,             :string ,   :null => true,    :limit => 1000
+      t.column :reporter,             :string,    :null => true,         :limit => 40
+      t.column :assignee,             :string,    :null => true,         :limit => 40
+      t.column :author_login,         :string,    :null => true,    :limit => 100
+      t.column :action_plan_key,      :string,    :null => true,    :limit => 50
+      t.column :issue_attributes,           :string,    :null => true,    :limit => 4000
+
+      # functional dates
+      t.column :issue_creation_date,  :datetime,  :null => true
+      t.column :issue_close_date,     :datetime,  :null => true
+      t.column :issue_update_date,    :datetime,  :null => true
+
+      # technical dates
+      t.column :created_at,           :datetime,  :null => true
+      t.column :updated_at,           :datetime,  :null => true
+    end
+
+    add_index :issues,  :kee,                 :name => 'issues_kee',         :unique => true
+    add_index :issues,  :resource_id,         :name => 'issues_resource_id'
+    add_index :issues,  :project_id,          :name => 'issues_project_id'
+    add_index :issues,  :rule_id,             :name => 'issues_rule_id'
+    add_index :issues,  :severity,            :name => 'issues_severity'
+    add_index :issues,  :status,              :name => 'issues_status'
+    add_index :issues,  :assignee,            :name => 'issues_assignee'
+    add_index :issues,  :action_plan_key,     :name => 'issues_action_plan_key'
+    add_index :issues,  :issue_creation_date, :name => 'issues_creation_date'
+  end
+
+end
+
diff --git a/sonar-server/src/main/webapp/WEB-INF/db/migrate/398_delete_review_duplications.rb b/sonar-server/src/main/webapp/WEB-INF/db/migrate/398_delete_review_duplications.rb
deleted file mode 100644 (file)
index d6b39a8..0000000
+++ /dev/null
@@ -1,42 +0,0 @@
-#
-# 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
-# See SONAR-4305
-#
-class DeleteReviewDuplications < ActiveRecord::Migration
-
-  class Review < ActiveRecord::Base
-  end
-
-  def self.up
-    duplicated_ids = ActiveRecord::Base.connection.select_rows('select rule_failure_permanent_id from reviews group by rule_failure_permanent_id having count(*) > 1')
-    say_with_time "Removing #{duplicated_ids.size} duplicated reviews" do
-      duplicated_ids.each do |id|
-        reviews = Review.find(:all, :conditions => {:rule_failure_permanent_id => id})
-        # delete all reviews except the last one
-        reviews[0...-1].each do |review|
-          Review.delete(review.id)
-        end
-      end
-    end
-  end
-end
\ No newline at end of file
diff --git a/sonar-server/src/main/webapp/WEB-INF/db/migrate/399_create_issue_changes.rb b/sonar-server/src/main/webapp/WEB-INF/db/migrate/399_create_issue_changes.rb
new file mode 100644 (file)
index 0000000..016380c
--- /dev/null
@@ -0,0 +1,42 @@
+#
+# 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 CreateIssueChanges < ActiveRecord::Migration
+
+  def self.up
+    create_table :issue_changes do |t|
+      t.column :kee,                :string,    :null => true,    :limit => 50
+      t.column :issue_key,          :string,    :null => false,   :limit => 50
+      t.column :user_login,         :string,    :null => true,   :limit => 40
+      t.column :change_type,                           :string,          :null => true,          :limit => 20
+      t.column :change_data,        :text,      :null => true
+      t.column :created_at,         :datetime,  :null => true
+      t.column :updated_at,         :datetime,  :null => true
+    end
+
+    add_index :issue_changes,  :kee,         :name => 'issue_changes_kee'
+    add_index :issue_changes,  :issue_key,   :name => 'issue_changes_issue_key'
+  end
+
+end
+
diff --git a/sonar-server/src/main/webapp/WEB-INF/db/migrate/399_migrate_violations_to_issues.rb b/sonar-server/src/main/webapp/WEB-INF/db/migrate/399_migrate_violations_to_issues.rb
deleted file mode 100644 (file)
index 256194a..0000000
+++ /dev/null
@@ -1,163 +0,0 @@
-#
-# 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
-# See SONAR-4305
-#
-class MigrateViolationsToIssues < ActiveRecord::Migration
-
-  class RuleFailure < ActiveRecord::Base
-  end
-
-  class Issue < ActiveRecord::Base
-  end
-
-  class IssueChange < ActiveRecord::Base
-  end
-
-  class User < ActiveRecord::Base
-  end
-
-  class ActionPlan < ActiveRecord::Base
-  end
-
-  PRIORITY_TO_SEVERITY = {1 => 'INFO', 2 => 'MINOR', 3 => 'MAJOR', 4 => 'CRITICAL', 5 => 'BLOCKER'}
-
-  def self.up
-    truncate_issues
-
-    violation_ids = ActiveRecord::Base.connection.select_rows('select id from rule_failures')
-
-    say_with_time "Converting #{violation_ids.size} violations to issues" do
-      logins_by_id = User.all.inject({}) do |result, user|
-        result[user.id]=user.login
-        result
-      end
-
-      plans_by_id = ActionPlan.all.inject({}) do |result, plan|
-        result[plan.id]=plan.kee
-        result
-      end
-
-      violation_ids.each_slice(999) do |ids|
-        violations = ActiveRecord::Base.connection.select_rows(sql_select_violation(ids))
-        ActiveRecord::Base.transaction do
-          violations.each do |violation|
-            issue_key = new_key
-            review_id = violation[0]
-            created_at = violation[7]
-            resource_id = violation[1]
-            if resource_id.present?
-              issue = Issue.new(
-                :kee => issue_key,
-                :resource_id => violation[1],
-                :rule_id => violation[2],
-                :severity => PRIORITY_TO_SEVERITY[violation[3].to_i],
-                :message => violation[4],
-                :line => violation[5],
-                :effort_to_fix => violation[6],
-                :resolution => violation[13],
-                :checksum => violation[8],
-                :author_login => nil,
-                :issue_attributes => violation[15],
-                :issue_creation_date => created_at,
-                :issue_close_date => nil,
-                :created_at => created_at
-              )
-              if review_id.present?
-                # has review
-                issue.status=violation[11]
-                issue.issue_update_date=violation[16]
-                issue.updated_at=violation[16]
-                issue.severity=violation[12]
-                issue.manual_severity=violation[14]
-                issue.reporter=logins_by_id[violation[9].to_i] if violation[9].present?
-                issue.assignee=logins_by_id[violation[10].to_i] if violation[10].present?
-
-                plan_id = select_plan_id(review_id)
-                issue.action_plan_key=plans_by_id[plan_id.to_i] if plan_id
-
-                review_comments = select_review_comments(review_id)
-                review_comments.each do |review_comment|
-                  user_id = review_comment[2]
-                  login = logins_by_id[user_id.to_i]
-                  if login
-                    IssueChange.create(
-                      :kee => new_key,
-                      :issue_key => issue_key,
-                      :user_login => login,
-                      :change_type => 'comment',
-                      :change_data => review_comment[3],
-                      :created_at => review_comment[0],
-                      :updated_at => review_comment[1]
-                    )
-                  end
-                end
-
-              else
-                # does not have review
-                issue.status='OPEN'
-                issue.issue_update_date=created_at
-                issue.updated_at=created_at
-                issue.manual_severity=false
-              end
-              issue.save
-            end
-          end
-        end
-      end
-    end
-
-    #TODO
-    #drop_table('rule_failures')
-    #drop_table('reviews')
-    #drop_table('review_comments')
-    #drop_table('action_plans_reviews')
-  end
-
-  def self.truncate_issues
-    ActiveRecord::Base.connection.execute('truncate table issues')
-    ActiveRecord::Base.connection.execute('truncate table issue_changes')
-  end
-
-  def self.sql_select_violation(ids)
-    "select rev.id, s.project_id, rf.rule_id, rf.failure_level, rf.message, rf.line, rf.cost, rf.created_at,
-           rf.checksum,
-           rev.user_id, rev.assignee_id, rev.status, rev.severity, rev.resolution, rev.manual_severity, rev.data,
-           rev.updated_at
-    from rule_failures rf
-    inner join snapshots s on s.id=rf.snapshot_id
-    left join reviews rev on rev.rule_failure_permanent_id=rf.permanent_id
-    where rf.id in (#{ids.join(',')})"
-  end
-
-  def self.new_key
-    Java::JavaUtil::UUID.randomUUID().toString()
-  end
-
-  def self.select_plan_id(review_id)
-    ActiveRecord::Base.connection.select_value("select action_plan_id from action_plans_reviews where review_id=#{review_id}")
-  end
-
-  def self.select_review_comments(review_id)
-    ActiveRecord::Base.connection.select_rows "select created_at, updated_at, user_id, review_text from review_comments where review_id=#{review_id}"
-  end
-end
\ No newline at end of file
diff --git a/sonar-server/src/main/webapp/WEB-INF/db/migrate/400_delete_review_duplications.rb b/sonar-server/src/main/webapp/WEB-INF/db/migrate/400_delete_review_duplications.rb
new file mode 100644 (file)
index 0000000..d6b39a8
--- /dev/null
@@ -0,0 +1,42 @@
+#
+# 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
+# See SONAR-4305
+#
+class DeleteReviewDuplications < ActiveRecord::Migration
+
+  class Review < ActiveRecord::Base
+  end
+
+  def self.up
+    duplicated_ids = ActiveRecord::Base.connection.select_rows('select rule_failure_permanent_id from reviews group by rule_failure_permanent_id having count(*) > 1')
+    say_with_time "Removing #{duplicated_ids.size} duplicated reviews" do
+      duplicated_ids.each do |id|
+        reviews = Review.find(:all, :conditions => {:rule_failure_permanent_id => id})
+        # delete all reviews except the last one
+        reviews[0...-1].each do |review|
+          Review.delete(review.id)
+        end
+      end
+    end
+  end
+end
\ No newline at end of file
diff --git a/sonar-server/src/main/webapp/WEB-INF/db/migrate/401_migrate_violations_to_issues.rb b/sonar-server/src/main/webapp/WEB-INF/db/migrate/401_migrate_violations_to_issues.rb
new file mode 100644 (file)
index 0000000..256194a
--- /dev/null
@@ -0,0 +1,163 @@
+#
+# 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
+# See SONAR-4305
+#
+class MigrateViolationsToIssues < ActiveRecord::Migration
+
+  class RuleFailure < ActiveRecord::Base
+  end
+
+  class Issue < ActiveRecord::Base
+  end
+
+  class IssueChange < ActiveRecord::Base
+  end
+
+  class User < ActiveRecord::Base
+  end
+
+  class ActionPlan < ActiveRecord::Base
+  end
+
+  PRIORITY_TO_SEVERITY = {1 => 'INFO', 2 => 'MINOR', 3 => 'MAJOR', 4 => 'CRITICAL', 5 => 'BLOCKER'}
+
+  def self.up
+    truncate_issues
+
+    violation_ids = ActiveRecord::Base.connection.select_rows('select id from rule_failures')
+
+    say_with_time "Converting #{violation_ids.size} violations to issues" do
+      logins_by_id = User.all.inject({}) do |result, user|
+        result[user.id]=user.login
+        result
+      end
+
+      plans_by_id = ActionPlan.all.inject({}) do |result, plan|
+        result[plan.id]=plan.kee
+        result
+      end
+
+      violation_ids.each_slice(999) do |ids|
+        violations = ActiveRecord::Base.connection.select_rows(sql_select_violation(ids))
+        ActiveRecord::Base.transaction do
+          violations.each do |violation|
+            issue_key = new_key
+            review_id = violation[0]
+            created_at = violation[7]
+            resource_id = violation[1]
+            if resource_id.present?
+              issue = Issue.new(
+                :kee => issue_key,
+                :resource_id => violation[1],
+                :rule_id => violation[2],
+                :severity => PRIORITY_TO_SEVERITY[violation[3].to_i],
+                :message => violation[4],
+                :line => violation[5],
+                :effort_to_fix => violation[6],
+                :resolution => violation[13],
+                :checksum => violation[8],
+                :author_login => nil,
+                :issue_attributes => violation[15],
+                :issue_creation_date => created_at,
+                :issue_close_date => nil,
+                :created_at => created_at
+              )
+              if review_id.present?
+                # has review
+                issue.status=violation[11]
+                issue.issue_update_date=violation[16]
+                issue.updated_at=violation[16]
+                issue.severity=violation[12]
+                issue.manual_severity=violation[14]
+                issue.reporter=logins_by_id[violation[9].to_i] if violation[9].present?
+                issue.assignee=logins_by_id[violation[10].to_i] if violation[10].present?
+
+                plan_id = select_plan_id(review_id)
+                issue.action_plan_key=plans_by_id[plan_id.to_i] if plan_id
+
+                review_comments = select_review_comments(review_id)
+                review_comments.each do |review_comment|
+                  user_id = review_comment[2]
+                  login = logins_by_id[user_id.to_i]
+                  if login
+                    IssueChange.create(
+                      :kee => new_key,
+                      :issue_key => issue_key,
+                      :user_login => login,
+                      :change_type => 'comment',
+                      :change_data => review_comment[3],
+                      :created_at => review_comment[0],
+                      :updated_at => review_comment[1]
+                    )
+                  end
+                end
+
+              else
+                # does not have review
+                issue.status='OPEN'
+                issue.issue_update_date=created_at
+                issue.updated_at=created_at
+                issue.manual_severity=false
+              end
+              issue.save
+            end
+          end
+        end
+      end
+    end
+
+    #TODO
+    #drop_table('rule_failures')
+    #drop_table('reviews')
+    #drop_table('review_comments')
+    #drop_table('action_plans_reviews')
+  end
+
+  def self.truncate_issues
+    ActiveRecord::Base.connection.execute('truncate table issues')
+    ActiveRecord::Base.connection.execute('truncate table issue_changes')
+  end
+
+  def self.sql_select_violation(ids)
+    "select rev.id, s.project_id, rf.rule_id, rf.failure_level, rf.message, rf.line, rf.cost, rf.created_at,
+           rf.checksum,
+           rev.user_id, rev.assignee_id, rev.status, rev.severity, rev.resolution, rev.manual_severity, rev.data,
+           rev.updated_at
+    from rule_failures rf
+    inner join snapshots s on s.id=rf.snapshot_id
+    left join reviews rev on rev.rule_failure_permanent_id=rf.permanent_id
+    where rf.id in (#{ids.join(',')})"
+  end
+
+  def self.new_key
+    Java::JavaUtil::UUID.randomUUID().toString()
+  end
+
+  def self.select_plan_id(review_id)
+    ActiveRecord::Base.connection.select_value("select action_plan_id from action_plans_reviews where review_id=#{review_id}")
+  end
+
+  def self.select_review_comments(review_id)
+    ActiveRecord::Base.connection.select_rows "select created_at, updated_at, user_id, review_text from review_comments where review_id=#{review_id}"
+  end
+end
\ No newline at end of file
diff --git a/sonar-server/src/test/java/org/sonar/server/issue/ServerIssueStorageTest.java b/sonar-server/src/test/java/org/sonar/server/issue/ServerIssueStorageTest.java
new file mode 100644 (file)
index 0000000..18eb7c4
--- /dev/null
@@ -0,0 +1,114 @@
+/*
+ * 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.server.issue;
+
+import org.junit.Test;
+import org.sonar.api.rule.RuleKey;
+import org.sonar.api.rules.Rule;
+import org.sonar.api.rules.RuleFinder;
+import org.sonar.api.rules.RuleQuery;
+import org.sonar.core.issue.DefaultIssue;
+import org.sonar.core.persistence.AbstractDaoTestCase;
+import org.sonar.core.resource.ResourceDao;
+
+import java.util.Collection;
+
+import static org.fest.assertions.Assertions.assertThat;
+import static org.fest.assertions.Fail.fail;
+
+public class ServerIssueStorageTest extends AbstractDaoTestCase {
+
+  @Test
+  public void should_load_component_id_from_db() throws Exception {
+    setupData("should_load_component_id_from_db");
+
+    ServerIssueStorage storage = new ServerIssueStorage(getMyBatis(), new FakeRuleFinder(), new ResourceDao(getMyBatis()));
+    int componentId = storage.componentId(new DefaultIssue().setComponentKey("struts:Action.java"));
+
+    assertThat(componentId).isEqualTo(123);
+  }
+
+  @Test
+  public void should_fail_to_load_component_id_if_unknown_component() throws Exception {
+    setupData("should_fail_to_load_component_id_if_unknown_component");
+
+    ServerIssueStorage storage = new ServerIssueStorage(getMyBatis(), new FakeRuleFinder(), new ResourceDao(getMyBatis()));
+    try {
+      storage.componentId(new DefaultIssue().setComponentKey("struts:Action.java"));
+      fail();
+    } catch (Exception e) {
+      assertThat(e).hasMessage("Unknown component: struts:Action.java");
+    }
+  }
+
+  @Test
+  public void should_load_project_id_from_db() throws Exception {
+    setupData("should_load_project_id_from_db");
+
+    ServerIssueStorage storage = new ServerIssueStorage(getMyBatis(), new FakeRuleFinder(), new ResourceDao(getMyBatis()));
+    int projectId = storage.projectId(new DefaultIssue().setComponentKey("struts:Action.java"));
+
+    assertThat(projectId).isEqualTo(1);
+  }
+
+  @Test
+  public void should_fail_to_load_project_id_if_unknown_component() throws Exception {
+    setupData("should_fail_to_load_project_id_if_unknown_component");
+
+    ServerIssueStorage storage = new ServerIssueStorage(getMyBatis(), new FakeRuleFinder(), new ResourceDao(getMyBatis()));
+    try {
+      storage.projectId(new DefaultIssue().setComponentKey("struts:Action.java"));
+      fail();
+    } catch (Exception e) {
+      assertThat(e).hasMessage("Unknown component: struts:Action.java");
+    }
+  }
+
+  static class FakeRuleFinder implements RuleFinder {
+
+    @Override
+    public Rule findById(int ruleId) {
+      return null;
+    }
+
+    @Override
+    public Rule findByKey(String repositoryKey, String key) {
+      return null;
+    }
+
+    @Override
+    public Rule findByKey(RuleKey key) {
+      Rule rule = Rule.create().setRepositoryKey(key.repository()).setKey(key.rule());
+      rule.setId(200);
+      return rule;
+    }
+
+    @Override
+    public Rule find(RuleQuery query) {
+      return null;
+    }
+
+    @Override
+    public Collection<Rule> findAll(RuleQuery query) {
+      return null;
+    }
+  }
+}
diff --git a/sonar-server/src/test/resources/org/sonar/server/issue/ServerIssueStorageTest/should_fail_to_load_component_id_if_unknown_component.xml b/sonar-server/src/test/resources/org/sonar/server/issue/ServerIssueStorageTest/should_fail_to_load_component_id_if_unknown_component.xml
new file mode 100644 (file)
index 0000000..5ed00ba
--- /dev/null
@@ -0,0 +1 @@
+<dataset></dataset>
\ No newline at end of file
diff --git a/sonar-server/src/test/resources/org/sonar/server/issue/ServerIssueStorageTest/should_fail_to_load_project_id_if_unknown_component.xml b/sonar-server/src/test/resources/org/sonar/server/issue/ServerIssueStorageTest/should_fail_to_load_project_id_if_unknown_component.xml
new file mode 100644 (file)
index 0000000..5ed00ba
--- /dev/null
@@ -0,0 +1 @@
+<dataset></dataset>
\ No newline at end of file
diff --git a/sonar-server/src/test/resources/org/sonar/server/issue/ServerIssueStorageTest/should_load_component_id_from_db.xml b/sonar-server/src/test/resources/org/sonar/server/issue/ServerIssueStorageTest/should_load_component_id_from_db.xml
new file mode 100644 (file)
index 0000000..cf7e75f
--- /dev/null
@@ -0,0 +1,3 @@
+<dataset>
+  <projects id="123" kee="struts:Action.java" />
+</dataset>
\ No newline at end of file
diff --git a/sonar-server/src/test/resources/org/sonar/server/issue/ServerIssueStorageTest/should_load_project_id_from_db.xml b/sonar-server/src/test/resources/org/sonar/server/issue/ServerIssueStorageTest/should_load_project_id_from_db.xml
new file mode 100644 (file)
index 0000000..315634f
--- /dev/null
@@ -0,0 +1,7 @@
+<dataset>
+  <projects id="1" kee="struts" root_id="[null]"/>
+  <projects id="2" kee="struts:Action.java" root_id="1"/>
+
+  <snapshots id="1" project_id="1" parent_snapshot_id="[null]" root_project_id="1" root_snapshot_id="[null]" />
+  <snapshots id="2" project_id="2" parent_snapshot_id="1" root_project_id="1" root_snapshot_id="1" />
+</dataset>
\ No newline at end of file