]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-3755 Manage action plan management console with Java instead of Ruby
authorJulien Lancelot <julien.lancelot@gmail.com>
Tue, 7 May 2013 14:23:01 +0000 (16:23 +0200)
committerJulien Lancelot <julien.lancelot@gmail.com>
Tue, 7 May 2013 14:23:11 +0000 (16:23 +0200)
26 files changed:
sonar-core/src/main/java/org/sonar/core/issue/ActionPlanFinder.java [deleted file]
sonar-core/src/main/java/org/sonar/core/issue/ActionPlanManager.java [new file with mode: 0644]
sonar-core/src/main/java/org/sonar/core/issue/DefaultActionPlan.java
sonar-core/src/main/java/org/sonar/core/issue/db/ActionPlanDao.java
sonar-core/src/main/java/org/sonar/core/issue/db/ActionPlanDto.java
sonar-core/src/main/java/org/sonar/core/issue/db/ActionPlanMapper.java
sonar-core/src/main/resources/org/sonar/core/issue/db/ActionPlanMapper.xml
sonar-core/src/main/resources/org/sonar/core/persistence/schema-h2.ddl
sonar-core/src/test/java/org/sonar/core/issue/ActionPlanFinderTest.java [deleted file]
sonar-core/src/test/java/org/sonar/core/issue/ActionPlanManagerTest.java [new file with mode: 0644]
sonar-core/src/test/java/org/sonar/core/issue/db/ActionPlanDaoTest.java
sonar-core/src/test/resources/org/sonar/core/issue/db/ActionPlanDaoTest/should_delete_action_plan-result.xml [new file with mode: 0644]
sonar-core/src/test/resources/org/sonar/core/issue/db/ActionPlanDaoTest/should_delete_action_plan.xml [new file with mode: 0644]
sonar-core/src/test/resources/org/sonar/core/issue/db/ActionPlanDaoTest/should_insert_new_action_plan-result.xml [new file with mode: 0644]
sonar-core/src/test/resources/org/sonar/core/issue/db/ActionPlanDaoTest/should_update_action_plan-result.xml [new file with mode: 0644]
sonar-core/src/test/resources/org/sonar/core/issue/db/ActionPlanDaoTest/should_update_action_plan.xml [new file with mode: 0644]
sonar-plugin-api/src/main/java/org/sonar/api/issue/ActionPlan.java
sonar-server/src/main/java/org/sonar/server/issue/ServerIssueActions.java
sonar-server/src/main/java/org/sonar/server/issue/ServerIssueFinder.java
sonar-server/src/main/java/org/sonar/server/issue/WebIssuesInternal.java
sonar-server/src/main/java/org/sonar/server/platform/Platform.java
sonar-server/src/main/webapp/WEB-INF/app/controllers/api/action_plans_controller.rb [new file with mode: 0644]
sonar-server/src/main/webapp/WEB-INF/app/controllers/issue_controller.rb
sonar-server/src/main/webapp/WEB-INF/app/controllers/issues_action_plans_controller.rb
sonar-server/src/main/webapp/WEB-INF/app/views/issues_action_plans/_new.html.erb
sonar-server/src/test/java/org/sonar/server/issue/ServerIssueFinderTest.java

diff --git a/sonar-core/src/main/java/org/sonar/core/issue/ActionPlanFinder.java b/sonar-core/src/main/java/org/sonar/core/issue/ActionPlanFinder.java
deleted file mode 100644 (file)
index bd52732..0000000
+++ /dev/null
@@ -1,115 +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 com.google.common.base.Function;
-import com.google.common.collect.Iterables;
-import org.sonar.api.ServerComponent;
-import org.sonar.api.issue.ActionPlan;
-import org.sonar.core.issue.db.ActionPlanDao;
-import org.sonar.core.issue.db.ActionPlanDto;
-import org.sonar.core.issue.db.ActionPlanStatsDao;
-import org.sonar.core.issue.db.ActionPlanStatsDto;
-import org.sonar.core.resource.ResourceDao;
-import org.sonar.core.resource.ResourceDto;
-import org.sonar.core.resource.ResourceQuery;
-
-import javax.annotation.Nullable;
-
-import java.util.Collection;
-import java.util.List;
-
-import static com.google.common.collect.Lists.newArrayList;
-
-/**
- * @since 3.6
- */
-public class ActionPlanFinder implements ServerComponent {
-
-  private final ActionPlanDao actionPlanDao;
-  private final ActionPlanStatsDao actionPlanStatsDao;
-  private final ResourceDao resourceDao;
-
-  public ActionPlanFinder(ActionPlanDao actionPlanDao, ActionPlanStatsDao actionPlanStatsDao, ResourceDao resourceDao) {
-    this.actionPlanDao = actionPlanDao;
-    this.actionPlanStatsDao = actionPlanStatsDao;
-    this.resourceDao = resourceDao;
-  }
-
-  public Collection<ActionPlan> findByKeys(Collection<String> keys) {
-    Collection<ActionPlanDto> actionPlanDtos = actionPlanDao.findByKeys(keys);
-    return toActionPlans(actionPlanDtos);
-  }
-
-  public ActionPlan findByKey(String key) {
-    ActionPlanDto actionPlanDto = actionPlanDao.findByKey(key);
-    if (actionPlanDto == null) {
-      return null;
-    }
-    return actionPlanDto.toActionPlan();
-  }
-
-  public Collection<ActionPlan> findOpenByProjectKey(String projectKey) {
-    ResourceDto resourceDto = resourceDao.getResource(ResourceQuery.create().setKey(projectKey));
-    if (resourceDto == null) {
-      throw new IllegalArgumentException("Project " + projectKey + " has not been found.");
-    }
-    Collection<ActionPlanDto> actionPlanDtos = actionPlanDao.findOpenByProjectId(resourceDto.getId());
-    return toActionPlans(actionPlanDtos);
-  }
-
-  public List<ActionPlanStats> findOpenActionPlanStats(String projectKey) {
-    ResourceDto resourceDto = resourceDao.getResource(ResourceQuery.create().setKey(projectKey));
-    if (resourceDto == null) {
-      throw new IllegalArgumentException("Project " + projectKey + " does not exists.");
-    }
-    Collection<ActionPlanStatsDto> actionPlanStatsDtos = actionPlanStatsDao.findOpenByProjectId(resourceDto.getId());
-    return newArrayList(Iterables.transform(actionPlanStatsDtos, new Function<ActionPlanStatsDto, ActionPlanStats>() {
-      @Override
-      public ActionPlanStats apply(ActionPlanStatsDto actionPlanStatsDto) {
-        return actionPlanStatsDto.toActionPlanStat();
-      }
-    }));
-  }
-
-  public List<ActionPlanStats> findClosedActionPlanStats(String projectKey) {
-    ResourceDto resourceDto = resourceDao.getResource(ResourceQuery.create().setKey(projectKey));
-    if (resourceDto == null) {
-      throw new IllegalArgumentException("Project " + projectKey + " does not exists.");
-    }
-    Collection<ActionPlanStatsDto> actionPlanStatsDtos = actionPlanStatsDao.findClosedByProjectId(resourceDto.getId());
-    return newArrayList(Iterables.transform(actionPlanStatsDtos, new Function<ActionPlanStatsDto, ActionPlanStats>() {
-      @Override
-      public ActionPlanStats apply(ActionPlanStatsDto actionPlanStatsDto) {
-        return actionPlanStatsDto.toActionPlanStat();
-      }
-    }));
-  }
-
-  private Collection<ActionPlan> toActionPlans(Collection<ActionPlanDto> actionPlanDtos) {
-    return newArrayList(Iterables.transform(actionPlanDtos, new Function<ActionPlanDto, ActionPlan>() {
-      @Override
-      public ActionPlan apply(@Nullable ActionPlanDto actionPlanDto) {
-        return actionPlanDto.toActionPlan();
-      }
-    }));
-  }
-}
diff --git a/sonar-core/src/main/java/org/sonar/core/issue/ActionPlanManager.java b/sonar-core/src/main/java/org/sonar/core/issue/ActionPlanManager.java
new file mode 100644 (file)
index 0000000..56634d7
--- /dev/null
@@ -0,0 +1,141 @@
+/*
+ * 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 com.google.common.base.Function;
+import com.google.common.collect.Iterables;
+import org.sonar.api.ServerComponent;
+import org.sonar.api.issue.ActionPlan;
+import org.sonar.core.issue.db.ActionPlanDao;
+import org.sonar.core.issue.db.ActionPlanDto;
+import org.sonar.core.issue.db.ActionPlanStatsDao;
+import org.sonar.core.issue.db.ActionPlanStatsDto;
+import org.sonar.core.resource.ResourceDao;
+import org.sonar.core.resource.ResourceDto;
+import org.sonar.core.resource.ResourceQuery;
+
+import javax.annotation.Nullable;
+
+import java.util.Collection;
+import java.util.Date;
+import java.util.List;
+
+import static com.google.common.collect.Lists.newArrayList;
+
+/**
+ * @since 3.6
+ */
+public class ActionPlanManager implements ServerComponent {
+
+  private final ActionPlanDao actionPlanDao;
+  private final ActionPlanStatsDao actionPlanStatsDao;
+  private final ResourceDao resourceDao;
+
+  public ActionPlanManager(ActionPlanDao actionPlanDao, ActionPlanStatsDao actionPlanStatsDao, ResourceDao resourceDao) {
+    this.actionPlanDao = actionPlanDao;
+    this.actionPlanStatsDao = actionPlanStatsDao;
+    this.resourceDao = resourceDao;
+  }
+
+  public ActionPlan create(ActionPlan actionPlan, Integer projectId){
+    actionPlanDao.save(ActionPlanDto.toActionDto(actionPlan, projectId));
+    return actionPlan;
+  }
+
+  public ActionPlan update(DefaultActionPlan actionPlan, Integer projectId){
+    actionPlanDao.update(ActionPlanDto.toActionDto(actionPlan, projectId));
+    return actionPlan;
+  }
+
+  public void delete(String key){
+    actionPlanDao.delete(key);
+  }
+
+  public ActionPlan setStatus(String key, String status){
+    ActionPlanDto actionPlanDto = actionPlanDao.findByKey(key);
+    if (actionPlanDto == null) {
+      throw new IllegalArgumentException("Action plan " + key + " has not been found.");
+    }
+    actionPlanDto.setStatus(status);
+    actionPlanDto.setCreatedAt(new Date());
+    actionPlanDao.update(actionPlanDto);
+    return actionPlanDto.toActionPlan();
+  }
+
+  public ActionPlan findByKey(String key) {
+    ActionPlanDto actionPlanDto = actionPlanDao.findByKey(key);
+    if (actionPlanDto == null) {
+      return null;
+    }
+    return actionPlanDto.toActionPlan();
+  }
+
+  public Collection<ActionPlan> findByKeys(Collection<String> keys) {
+    Collection<ActionPlanDto> actionPlanDtos = actionPlanDao.findByKeys(keys);
+    return toActionPlans(actionPlanDtos);
+  }
+
+  public Collection<ActionPlan> findOpenByProjectKey(String projectKey) {
+    ResourceDto resourceDto = resourceDao.getResource(ResourceQuery.create().setKey(projectKey));
+    if (resourceDto == null) {
+      throw new IllegalArgumentException("Project " + projectKey + " has not been found.");
+    }
+    Collection<ActionPlanDto> actionPlanDtos = actionPlanDao.findOpenByProjectId(resourceDto.getId());
+    return toActionPlans(actionPlanDtos);
+  }
+
+  public List<ActionPlanStats> findOpenActionPlanStats(String projectKey) {
+    ResourceDto resourceDto = resourceDao.getResource(ResourceQuery.create().setKey(projectKey));
+    if (resourceDto == null) {
+      throw new IllegalArgumentException("Project " + projectKey + " does not exists.");
+    }
+    Collection<ActionPlanStatsDto> actionPlanStatsDtos = actionPlanStatsDao.findOpenByProjectId(resourceDto.getId());
+    return newArrayList(Iterables.transform(actionPlanStatsDtos, new Function<ActionPlanStatsDto, ActionPlanStats>() {
+      @Override
+      public ActionPlanStats apply(ActionPlanStatsDto actionPlanStatsDto) {
+        return actionPlanStatsDto.toActionPlanStat();
+      }
+    }));
+  }
+
+  public List<ActionPlanStats> findClosedActionPlanStats(String projectKey) {
+    ResourceDto resourceDto = resourceDao.getResource(ResourceQuery.create().setKey(projectKey));
+    if (resourceDto == null) {
+      throw new IllegalArgumentException("Project " + projectKey + " does not exists.");
+    }
+    Collection<ActionPlanStatsDto> actionPlanStatsDtos = actionPlanStatsDao.findClosedByProjectId(resourceDto.getId());
+    return newArrayList(Iterables.transform(actionPlanStatsDtos, new Function<ActionPlanStatsDto, ActionPlanStats>() {
+      @Override
+      public ActionPlanStats apply(ActionPlanStatsDto actionPlanStatsDto) {
+        return actionPlanStatsDto.toActionPlanStat();
+      }
+    }));
+  }
+
+  private Collection<ActionPlan> toActionPlans(Collection<ActionPlanDto> actionPlanDtos) {
+    return newArrayList(Iterables.transform(actionPlanDtos, new Function<ActionPlanDto, ActionPlan>() {
+      @Override
+      public ActionPlan apply(@Nullable ActionPlanDto actionPlanDto) {
+        return actionPlanDto.toActionPlan();
+      }
+    }));
+  }
+}
index 3e3b3b24608f2fec5b17d4fd46e9378468972f36..8c48c5c5f2c4221186ed4b21defa110398ed2714 100644 (file)
@@ -33,8 +33,8 @@ public class DefaultActionPlan implements ActionPlan {
   private String userLogin;
   private String status;
   private Date deadLine;
-  private Date creationDate;
-  private Date updateDate;
+  private Date createdAt;
+  private Date updatedAt;
 
   private DefaultActionPlan(){
 
@@ -46,7 +46,7 @@ public class DefaultActionPlan implements ActionPlan {
     Date now = new Date();
     actionPlan.setName(name);
     actionPlan.setStatus(ActionPlan.STATUS_OPEN);
-    actionPlan.setCreationDate(now).setUpdateDate(now);
+    actionPlan.setCreatedAt(now).setUpdatedAt(now);
     return actionPlan;
   }
 
@@ -104,21 +104,21 @@ public class DefaultActionPlan implements ActionPlan {
     return this;
   }
 
-  public Date creationDate() {
-    return creationDate;
+  public Date createdAt() {
+    return createdAt;
   }
 
-  public DefaultActionPlan setCreationDate(Date creationDate) {
-    this.creationDate = creationDate;
+  public DefaultActionPlan setCreatedAt(Date createdAt) {
+    this.createdAt = createdAt;
     return this;
   }
 
-  public Date updateDate() {
-    return updateDate;
+  public Date updatedAt() {
+    return updatedAt;
   }
 
-  public DefaultActionPlan setUpdateDate(Date updateDate) {
-    this.updateDate = updateDate;
+  public DefaultActionPlan setUpdatedAt(Date updatedAt) {
+    this.updatedAt = updatedAt;
     return this;
   }
 }
index ceff877ce81bdc8a01b1ef357063ced0b827b1d7..6c90a39bf05a626587a9e924553172e0ee8a5e15 100644 (file)
@@ -43,6 +43,36 @@ public class ActionPlanDao implements BatchComponent, ServerComponent {
     this.mybatis = mybatis;
   }
 
+  public void save(ActionPlanDto actionPlanDto) {
+    SqlSession session = mybatis.openSession();
+    try {
+      session.getMapper(ActionPlanMapper.class).insert(actionPlanDto);
+      session.commit();
+    } finally {
+      MyBatis.closeQuietly(session);
+    }
+  }
+
+  public void update(ActionPlanDto actionPlanDto) {
+    SqlSession session = mybatis.openSession();
+    try {
+      session.getMapper(ActionPlanMapper.class).update(actionPlanDto);
+      session.commit();
+    } finally {
+      MyBatis.closeQuietly(session);
+    }
+  }
+
+  public void delete(String key) {
+    SqlSession session = mybatis.openSession();
+    try {
+      session.getMapper(ActionPlanMapper.class).delete(key);
+      session.commit();
+    } finally {
+      MyBatis.closeQuietly(session);
+    }
+  }
+
   public ActionPlanDto findByKey(String key) {
     SqlSession session = mybatis.openSession();
     try {
index 9727acae0e9958bacc51afc401512489211c262c..cbdade5f63349c1a743c0abad098084d9b149ff6 100644 (file)
@@ -22,6 +22,7 @@ package org.sonar.core.issue.db;
 
 import org.apache.commons.lang.builder.ToStringBuilder;
 import org.apache.commons.lang.builder.ToStringStyle;
+import org.sonar.api.issue.ActionPlan;
 import org.sonar.core.issue.DefaultActionPlan;
 
 import java.util.Date;
@@ -162,7 +163,19 @@ public class ActionPlanDto {
       .setStatus(status)
       .setDeadLine(deadLine)
       .setUserLogin(userLogin)
-      .setCreationDate(createdAt)
-      .setUpdateDate(updatedAt);
+      .setCreatedAt(createdAt)
+      .setUpdatedAt(updatedAt);
+  }
+
+  public static ActionPlanDto toActionDto(ActionPlan actionPlan, Integer projectId) {
+    return new ActionPlanDto().setKey(actionPlan.key())
+             .setName(actionPlan.name())
+             .setProjectId(projectId)
+             .setDescription(actionPlan.description())
+             .setStatus(actionPlan.status())
+             .setDeadLine(actionPlan.deadLine())
+             .setUserLogin(actionPlan.userLogin())
+             .setCreatedAt(actionPlan.createdAt())
+             .setUpdatedAt(actionPlan.updatedAt());
   }
 }
index b167fdc7f0a829f38cd94f28b568e1107da39d0d..6d28aaee4791343b9372ee5d793d0795c5c4b36e 100644 (file)
@@ -30,18 +30,15 @@ import java.util.List;
  */
 public interface ActionPlanMapper {
 
-  /**
-   * @since3.6
-   */
+  void insert(ActionPlanDto actionPlanDto);
+
+  void update(ActionPlanDto actionPlanDto);
+
+  void delete(@Param("key") String key);
+
   Collection<ActionPlanDto> findByKeys(@Param("keys") List <List<String>> keys);
 
-  /**
-   * @since3.6
-   */
   ActionPlanDto findByKey(@Param("key") String key);
 
-  /**
-   * @since3.6
-   */
   Collection<ActionPlanDto> findOpenByProjectId(@Param("projectId") Long projectId);
 }
index 973ed87acf1b6726e7a1db3bb48275684ae9d35a..3e9f7b85b200cf746782a9ddb8594d0709d22ed3 100644 (file)
     ap.updated_at as updatedAt
   </sql>
 
+  <insert id="insert" parameterType="ActionPlanIssue" useGeneratedKeys="true" keyProperty="id">
+    INSERT INTO action_plans (kee, name, description, user_login, project_id, status, deadline, created_at, updated_at)
+    VALUES (#{kee}, #{name}, #{description}, #{userLogin}, #{projectId}, #{status}, #{deadLine}, #{createdAt}, #{updatedAt})
+  </insert>
+
+  <!-- Oracle -->
+  <insert id="insert" databaseId="oracle" parameterType="ActionPlanIssue" keyColumn="id" useGeneratedKeys="true" keyProperty="id">
+    <selectKey order="BEFORE" resultType="Long" keyProperty="id">
+      select issues_seq.NEXTVAL from DUAL
+    </selectKey>
+    INSERT INTO action_plans (id, kee, name, description, user_login, project_id, status, deadline, created_at, updated_at)
+    VALUES (#{id}, #{kee}, #{name}, #{description}, #{userLogin}, #{projectId}, #{status}, #{deadLine}, #{createdAt}, #{updatedAt})
+  </insert>
+
+  <update id="update" parameterType="ActionPlanIssue">
+    update action_plans set
+    name=#{name},
+    description=#{description},
+    user_login=#{userLogin},
+    project_id=#{projectId},
+    status=#{status},
+    deadline=#{deadLine},
+    updated_at=current_timestamp
+    where kee = #{kee}
+  </update>
+
+  <delete id="delete" parameterType="String">
+    delete from action_plans where kee=#{key}
+  </delete>
+
   <select id="findByKey" parameterType="long" resultType="ActionPlanIssue">
     select <include refid="actionPlanColumns"/>
     from action_plans ap
index a3c6e57e61d0db19ad82916c3b6bcf9597b46bb1..54fadc6212c169a31d13a2f384111fb051e79244 100644 (file)
@@ -434,7 +434,6 @@ CREATE TABLE "RESOURCE_INDEX" (
 
 CREATE TABLE "ACTION_PLANS" (
   "ID" INTEGER NOT NULL GENERATED BY DEFAULT AS IDENTITY (START WITH 1, INCREMENT BY 1),
-  "USER_ID" INTEGER,
   "KEE" VARCHAR(100),
   "NAME" VARCHAR(200),
   "DESCRIPTION" VARCHAR(1000),
diff --git a/sonar-core/src/test/java/org/sonar/core/issue/ActionPlanFinderTest.java b/sonar-core/src/test/java/org/sonar/core/issue/ActionPlanFinderTest.java
deleted file mode 100644 (file)
index 7ae39c5..0000000
+++ /dev/null
@@ -1,123 +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 org.junit.Before;
-import org.junit.Test;
-import org.sonar.api.issue.ActionPlan;
-import org.sonar.core.issue.db.ActionPlanDao;
-import org.sonar.core.issue.db.ActionPlanDto;
-import org.sonar.core.issue.db.ActionPlanStatsDao;
-import org.sonar.core.issue.db.ActionPlanStatsDto;
-import org.sonar.core.resource.ResourceDao;
-import org.sonar.core.resource.ResourceDto;
-import org.sonar.core.resource.ResourceQuery;
-
-import java.util.Collection;
-
-import static com.google.common.collect.Lists.newArrayList;
-import static org.fest.assertions.Assertions.assertThat;
-import static org.mockito.Matchers.any;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.when;
-
-public class ActionPlanFinderTest {
-
-  private ActionPlanDao actionPlanDao = mock(ActionPlanDao.class);
-  private ActionPlanStatsDao actionPlanStatsDao = mock(ActionPlanStatsDao.class);
-  private ResourceDao resourceDao = mock(ResourceDao.class);
-  private ActionPlanFinder actionPlanFinder;
-
-  @Before
-  public void before() {
-    actionPlanFinder = new ActionPlanFinder(actionPlanDao, actionPlanStatsDao, resourceDao);
-  }
-
-  @Test
-  public void should_find_by_key() {
-    when(actionPlanDao.findByKey("ABCD")).thenReturn(new ActionPlanDto().setKey("ABCD"));
-    ActionPlan result = actionPlanFinder.findByKey("ABCD");
-    assertThat(result).isNotNull();
-    assertThat(result.key()).isEqualTo("ABCD");
-  }
-
-  @Test
-  public void should_return_null_if_no_action_plan_when_find_by_key() {
-    when(actionPlanDao.findByKey("ABCD")).thenReturn(null);
-    assertThat(actionPlanFinder.findByKey("ABCD")).isNull();
-  }
-
-  @Test
-  public void should_find_by_keys() {
-    when(actionPlanDao.findByKeys(newArrayList("ABCD"))).thenReturn(newArrayList(new ActionPlanDto().setKey("ABCD")));
-    Collection<ActionPlan> results = actionPlanFinder.findByKeys(newArrayList("ABCD"));
-    assertThat(results).hasSize(1);
-    assertThat(results.iterator().next().key()).isEqualTo("ABCD");
-  }
-
-  @Test
-  public void should_find_open_by_project_key() {
-    when(resourceDao.getResource(any(ResourceQuery.class))).thenReturn(new ResourceDto().setKey("org.sonar.Sample").setId(1l));
-    when(actionPlanDao.findOpenByProjectId(1l)).thenReturn(newArrayList(new ActionPlanDto().setKey("ABCD")));
-    Collection<ActionPlan> results = actionPlanFinder.findOpenByProjectKey("org.sonar.Sample");
-    assertThat(results).hasSize(1);
-    assertThat(results.iterator().next().key()).isEqualTo("ABCD");
-  }
-
-  @Test(expected = IllegalArgumentException.class)
-  public void should_throw_exception_if_project_not_found_when_find_open_by_project_key() {
-    when(resourceDao.getResource(any(ResourceQuery.class))).thenReturn(null);
-    actionPlanFinder.findOpenByProjectKey("<Unkown>");
-  }
-
-  @Test
-  public void should_find_open_action_plan_stats(){
-    when(resourceDao.getResource(any(ResourceQuery.class))).thenReturn(new ResourceDto().setId(1L).setKey("org.sonar.Sample"));
-    when(actionPlanStatsDao.findOpenByProjectId(1L)).thenReturn(newArrayList(new ActionPlanStatsDto()));
-
-    Collection<ActionPlanStats> results = actionPlanFinder.findOpenActionPlanStats("org.sonar.Sample");
-    assertThat(results).hasSize(1);
-  }
-
-  @Test(expected = IllegalArgumentException.class)
-  public void should_throw_exception_if_project_not_found_when_find_open_action_plan_stats(){
-    when(resourceDao.getResource(any(ResourceQuery.class))).thenReturn(null);
-
-    actionPlanFinder.findOpenActionPlanStats("org.sonar.Sample");
-  }
-
-  @Test
-  public void should_find_closed_action_plan_stats(){
-    when(resourceDao.getResource(any(ResourceQuery.class))).thenReturn(new ResourceDto().setId(1L).setKey("org.sonar.Sample"));
-    when(actionPlanStatsDao.findClosedByProjectId(1L)).thenReturn(newArrayList(new ActionPlanStatsDto()));
-
-    Collection<ActionPlanStats> results = actionPlanFinder.findClosedActionPlanStats("org.sonar.Sample");
-    assertThat(results).hasSize(1);
-  }
-
-  @Test(expected = IllegalArgumentException.class)
-  public void should_throw_exception_if_project_not_found_when_find_closed_action_plan_stats(){
-    when(resourceDao.getResource(any(ResourceQuery.class))).thenReturn(null);
-
-    actionPlanFinder.findClosedActionPlanStats("org.sonar.Sample");
-  }
-
-}
diff --git a/sonar-core/src/test/java/org/sonar/core/issue/ActionPlanManagerTest.java b/sonar-core/src/test/java/org/sonar/core/issue/ActionPlanManagerTest.java
new file mode 100644 (file)
index 0000000..59915d3
--- /dev/null
@@ -0,0 +1,147 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2013 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * SonarQube is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+
+package org.sonar.core.issue;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.sonar.api.issue.ActionPlan;
+import org.sonar.core.issue.db.ActionPlanDao;
+import org.sonar.core.issue.db.ActionPlanDto;
+import org.sonar.core.issue.db.ActionPlanStatsDao;
+import org.sonar.core.issue.db.ActionPlanStatsDto;
+import org.sonar.core.resource.ResourceDao;
+import org.sonar.core.resource.ResourceDto;
+import org.sonar.core.resource.ResourceQuery;
+
+import java.util.Collection;
+
+import static com.google.common.collect.Lists.newArrayList;
+import static org.fest.assertions.Assertions.assertThat;
+import static org.mockito.Matchers.any;
+import static org.mockito.Mockito.*;
+
+public class ActionPlanManagerTest {
+
+  private ActionPlanDao actionPlanDao = mock(ActionPlanDao.class);
+  private ActionPlanStatsDao actionPlanStatsDao = mock(ActionPlanStatsDao.class);
+  private ResourceDao resourceDao = mock(ResourceDao.class);
+  private ActionPlanManager actionPlanManager;
+
+  @Before
+  public void before() {
+    actionPlanManager = new ActionPlanManager(actionPlanDao, actionPlanStatsDao, resourceDao);
+  }
+
+  @Test
+  public void should_create() {
+    ActionPlan actionPlan = DefaultActionPlan.create("Long term");
+
+    actionPlanManager.create(actionPlan, 1);
+    verify(actionPlanDao).save(any(ActionPlanDto.class));
+  }
+
+  @Test
+  public void should_set_status() {
+    when(actionPlanDao.findByKey("ABCD")).thenReturn(new ActionPlanDto().setKey("ABCD"));
+
+    ActionPlan result = actionPlanManager.setStatus("ABCD", "CLOSED");
+    verify(actionPlanDao).update(any(ActionPlanDto.class));
+
+    assertThat(result).isNotNull();
+    assertThat(result.status()).isEqualTo("CLOSED");
+  }
+
+  @Test
+  public void should_delete() {
+    actionPlanManager.delete("ABCD");
+    verify(actionPlanDao).delete("ABCD");
+  }
+
+  @Test
+  public void should_find_by_key() {
+    when(actionPlanDao.findByKey("ABCD")).thenReturn(new ActionPlanDto().setKey("ABCD"));
+    ActionPlan result = actionPlanManager.findByKey("ABCD");
+    assertThat(result).isNotNull();
+    assertThat(result.key()).isEqualTo("ABCD");
+  }
+
+  @Test
+  public void should_return_null_if_no_action_plan_when_find_by_key() {
+    when(actionPlanDao.findByKey("ABCD")).thenReturn(null);
+    assertThat(actionPlanManager.findByKey("ABCD")).isNull();
+  }
+
+  @Test
+  public void should_find_by_keys() {
+    when(actionPlanDao.findByKeys(newArrayList("ABCD"))).thenReturn(newArrayList(new ActionPlanDto().setKey("ABCD")));
+    Collection<ActionPlan> results = actionPlanManager.findByKeys(newArrayList("ABCD"));
+    assertThat(results).hasSize(1);
+    assertThat(results.iterator().next().key()).isEqualTo("ABCD");
+  }
+
+  @Test
+  public void should_find_open_by_project_key() {
+    when(resourceDao.getResource(any(ResourceQuery.class))).thenReturn(new ResourceDto().setKey("org.sonar.Sample").setId(1l));
+    when(actionPlanDao.findOpenByProjectId(1l)).thenReturn(newArrayList(new ActionPlanDto().setKey("ABCD")));
+    Collection<ActionPlan> results = actionPlanManager.findOpenByProjectKey("org.sonar.Sample");
+    assertThat(results).hasSize(1);
+    assertThat(results.iterator().next().key()).isEqualTo("ABCD");
+  }
+
+  @Test(expected = IllegalArgumentException.class)
+  public void should_throw_exception_if_project_not_found_when_find_open_by_project_key() {
+    when(resourceDao.getResource(any(ResourceQuery.class))).thenReturn(null);
+    actionPlanManager.findOpenByProjectKey("<Unkown>");
+  }
+
+  @Test
+  public void should_find_open_action_plan_stats(){
+    when(resourceDao.getResource(any(ResourceQuery.class))).thenReturn(new ResourceDto().setId(1L).setKey("org.sonar.Sample"));
+    when(actionPlanStatsDao.findOpenByProjectId(1L)).thenReturn(newArrayList(new ActionPlanStatsDto()));
+
+    Collection<ActionPlanStats> results = actionPlanManager.findOpenActionPlanStats("org.sonar.Sample");
+    assertThat(results).hasSize(1);
+  }
+
+  @Test(expected = IllegalArgumentException.class)
+  public void should_throw_exception_if_project_not_found_when_find_open_action_plan_stats(){
+    when(resourceDao.getResource(any(ResourceQuery.class))).thenReturn(null);
+
+    actionPlanManager.findOpenActionPlanStats("org.sonar.Sample");
+  }
+
+  @Test
+  public void should_find_closed_action_plan_stats(){
+    when(resourceDao.getResource(any(ResourceQuery.class))).thenReturn(new ResourceDto().setId(1L).setKey("org.sonar.Sample"));
+    when(actionPlanStatsDao.findClosedByProjectId(1L)).thenReturn(newArrayList(new ActionPlanStatsDto()));
+
+    Collection<ActionPlanStats> results = actionPlanManager.findClosedActionPlanStats("org.sonar.Sample");
+    assertThat(results).hasSize(1);
+  }
+
+  @Test(expected = IllegalArgumentException.class)
+  public void should_throw_exception_if_project_not_found_when_find_closed_action_plan_stats(){
+    when(resourceDao.getResource(any(ResourceQuery.class))).thenReturn(null);
+
+    actionPlanManager.findClosedActionPlanStats("org.sonar.Sample");
+  }
+
+}
index 53e2d0684ba3ead3c394bf07e02968cbd83cc2f4..b21ebcbe4a5942bd657f1e44379265257ad96db8 100644 (file)
@@ -38,6 +38,36 @@ public class ActionPlanDaoTest extends AbstractDaoTestCase {
     dao = new ActionPlanDao(getMyBatis());
   }
 
+  @Test
+  public void should_insert_new_action_plan() {
+    ActionPlanDto actionPlanDto = new ActionPlanDto().setKey("ABC").setName("Long term").setDescription("Long term action plan").setStatus("OPEN")
+                                    .setProjectId(1).setUserLogin("arthur");
+
+    dao.save(actionPlanDto);
+
+    checkTables("should_insert_new_action_plan", new String[]{"id", "created_at", "updated_at"}, "action_plans");
+  }
+
+  @Test
+  public void should_update_action_plan() {
+    setupData("should_update_action_plan");
+
+    ActionPlanDto actionPlanDto = new ActionPlanDto().setKey("ABC").setName("Long term").setDescription("Long term action plan").setStatus("OPEN")
+                                    .setProjectId(1).setUserLogin("arthur");
+    dao.update(actionPlanDto);
+
+    checkTables("should_update_action_plan", new String[]{"id", "created_at", "updated_at"}, "action_plans");
+  }
+
+  @Test
+  public void should_delete_action_plan() {
+    setupData("should_delete_action_plan");
+
+    dao.delete("BCD");
+
+    checkTables("should_delete_action_plan", new String[]{"id", "created_at", "updated_at"}, "action_plans");
+  }
+
   @Test
   public void should_find_by_key() {
     setupData("should_find_by_key");
diff --git a/sonar-core/src/test/resources/org/sonar/core/issue/db/ActionPlanDaoTest/should_delete_action_plan-result.xml b/sonar-core/src/test/resources/org/sonar/core/issue/db/ActionPlanDaoTest/should_delete_action_plan-result.xml
new file mode 100644 (file)
index 0000000..2e330ef
--- /dev/null
@@ -0,0 +1,6 @@
+<dataset>
+
+  <action_plans id="1" kee="ABC" project_id="1" name="Long term" description="Long term action plan" deadline="[null]"
+                user_login="arthur" status="OPEN" created_at="[null]" updated_at="[null]" />
+
+</dataset>
diff --git a/sonar-core/src/test/resources/org/sonar/core/issue/db/ActionPlanDaoTest/should_delete_action_plan.xml b/sonar-core/src/test/resources/org/sonar/core/issue/db/ActionPlanDaoTest/should_delete_action_plan.xml
new file mode 100644 (file)
index 0000000..eeb2dfd
--- /dev/null
@@ -0,0 +1,9 @@
+<dataset>
+
+  <action_plans id="1" kee="ABC" project_id="1" name="Long term" description="Long term action plan" deadline="[null]"
+                user_login="arthur" status="OPEN" created_at="[null]" updated_at="[null]" />
+
+  <action_plans id="2" kee="BCD" project_id="1" name="Short term" description="Short term action plan" deadline="[null]"
+                user_login="arthur" status="CLOSED" created_at="[null]" updated_at="[null]" />
+
+</dataset>
diff --git a/sonar-core/src/test/resources/org/sonar/core/issue/db/ActionPlanDaoTest/should_insert_new_action_plan-result.xml b/sonar-core/src/test/resources/org/sonar/core/issue/db/ActionPlanDaoTest/should_insert_new_action_plan-result.xml
new file mode 100644 (file)
index 0000000..2e330ef
--- /dev/null
@@ -0,0 +1,6 @@
+<dataset>
+
+  <action_plans id="1" kee="ABC" project_id="1" name="Long term" description="Long term action plan" deadline="[null]"
+                user_login="arthur" status="OPEN" created_at="[null]" updated_at="[null]" />
+
+</dataset>
diff --git a/sonar-core/src/test/resources/org/sonar/core/issue/db/ActionPlanDaoTest/should_update_action_plan-result.xml b/sonar-core/src/test/resources/org/sonar/core/issue/db/ActionPlanDaoTest/should_update_action_plan-result.xml
new file mode 100644 (file)
index 0000000..2e330ef
--- /dev/null
@@ -0,0 +1,6 @@
+<dataset>
+
+  <action_plans id="1" kee="ABC" project_id="1" name="Long term" description="Long term action plan" deadline="[null]"
+                user_login="arthur" status="OPEN" created_at="[null]" updated_at="[null]" />
+
+</dataset>
diff --git a/sonar-core/src/test/resources/org/sonar/core/issue/db/ActionPlanDaoTest/should_update_action_plan.xml b/sonar-core/src/test/resources/org/sonar/core/issue/db/ActionPlanDaoTest/should_update_action_plan.xml
new file mode 100644 (file)
index 0000000..983893d
--- /dev/null
@@ -0,0 +1,6 @@
+<dataset>
+
+  <action_plans id="1" kee="ABC" project_id="1" name="Old name" description="Old desc" deadline="[null]"
+                user_login="[null]" status="CLOSED" created_at="[null]" updated_at="[null]" />
+
+</dataset>
index 9f48199d5411b701f053d945c6cf5e2ed7a21e74..464622807f9db007f30f3f2252127e83db6415c6 100644 (file)
@@ -51,8 +51,8 @@ public interface ActionPlan extends Serializable {
   @CheckForNull
   Date deadLine() ;
 
-  Date creationDate();
+  Date createdAt();
 
-  Date updateDate();
+  Date updatedAt();
 
 }
index 931839eb6a0fd2606b8f5dede6ea65ed639d9de3..1c46d4cf406fec5033698e2d3d1e2bed85b288f8 100644 (file)
@@ -51,20 +51,20 @@ public class ServerIssueActions implements ServerComponent {
   private final IssueChangeDao issueChangeDao;
   private final IssueStorage issueStorage;
   private final AuthorizationDao authorizationDao;
-  private final ActionPlanFinder actionPlanFinder;
+  private final ActionPlanManager actionPlanManager;
 
   public ServerIssueActions(IssueWorkflow workflow,
                             IssueDao issueDao,
                             IssueStorage issueStorage,
                             AuthorizationDao authorizationDao,
-                            IssueUpdater issueUpdater, IssueChangeDao issueChangeDao, ActionPlanFinder actionPlanFinder) {
+                            IssueUpdater issueUpdater, IssueChangeDao issueChangeDao, ActionPlanManager actionPlanManager) {
     this.workflow = workflow;
     this.issueDao = issueDao;
     this.issueStorage = issueStorage;
     this.issueUpdater = issueUpdater;
     this.authorizationDao = authorizationDao;
     this.issueChangeDao = issueChangeDao;
-    this.actionPlanFinder = actionPlanFinder;
+    this.actionPlanManager = actionPlanManager;
   }
 
   public List<Transition> listTransitions(String issueKey, UserSession userSession) {
@@ -100,7 +100,7 @@ public class ServerIssueActions implements ServerComponent {
   }
 
   public Issue plan(String issueKey, @Nullable String actionPlanKey, UserSession userSession) {
-    if (!Strings.isNullOrEmpty(actionPlanKey) && actionPlanFinder.findByKey(actionPlanKey) == null) {
+    if (!Strings.isNullOrEmpty(actionPlanKey) && actionPlanManager.findByKey(actionPlanKey) == null) {
       throw new IllegalStateException("Unknown action plan: " + actionPlanKey);
     }
 
index 74f124932836dc40094a47fa3fcbac507369560f..94c567c863e3990371c57aed3c7063df08da3359 100644 (file)
@@ -31,7 +31,7 @@ import org.sonar.api.issue.*;
 import org.sonar.api.rule.RuleKey;
 import org.sonar.api.rules.Rule;
 import org.sonar.api.utils.Paging;
-import org.sonar.core.issue.ActionPlanFinder;
+import org.sonar.core.issue.ActionPlanManager;
 import org.sonar.core.issue.DefaultIssue;
 import org.sonar.core.issue.db.IssueChangeDao;
 import org.sonar.core.issue.db.IssueDao;
@@ -62,17 +62,17 @@ public class ServerIssueFinder implements IssueFinder {
   private final AuthorizationDao authorizationDao;
   private final DefaultRuleFinder ruleFinder;
   private final ResourceDao resourceDao;
-  private final ActionPlanFinder actionPlanFinder;
+  private final ActionPlanManager actionPlanManager;
 
   public ServerIssueFinder(MyBatis myBatis, IssueDao issueDao, AuthorizationDao authorizationDao,
                            DefaultRuleFinder ruleFinder, ResourceDao resourceDao,
-                           ActionPlanFinder actionPlanFinder) {
+                           ActionPlanManager actionPlanManager) {
     this.myBatis = myBatis;
     this.issueDao = issueDao;
     this.authorizationDao = authorizationDao;
     this.ruleFinder = ruleFinder;
     this.resourceDao = resourceDao;
-    this.actionPlanFinder = actionPlanFinder;
+    this.actionPlanManager = actionPlanManager;
   }
 
   public Results find(IssueQuery query, @Nullable Integer currentUserId, String role) {
@@ -155,7 +155,7 @@ public class ServerIssueFinder implements IssueFinder {
   }
 
   private Collection<ActionPlan> findActionPlans(Set<String> actionPlanKeys) {
-    return actionPlanFinder.findByKeys(actionPlanKeys);
+    return actionPlanManager.findByKeys(actionPlanKeys);
   }
 
   public Issue findByKey(String key) {
index c58222e50d436aa82a508f08c8917d31f16af4d1..a6590440c30a1920a06c2aee0a04759fe12cacae 100644 (file)
  */
 package org.sonar.server.issue;
 
+import com.google.common.primitives.Ints;
 import org.sonar.api.ServerComponent;
 import org.sonar.api.issue.ActionPlan;
 import org.sonar.api.issue.Issue;
 import org.sonar.api.issue.IssueComment;
 import org.sonar.api.rule.RuleKey;
+import org.sonar.api.utils.DateUtils;
 import org.sonar.core.issue.*;
 import org.sonar.core.issue.workflow.Transition;
 import org.sonar.server.platform.UserSession;
 
 import java.util.Collection;
+import java.util.Date;
 import java.util.List;
 import java.util.Map;
 
@@ -38,11 +41,11 @@ import java.util.Map;
 public class WebIssuesInternal implements ServerComponent {
 
   private final ServerIssueActions actions;
-  private final ActionPlanFinder actionPlanFinder;
+  private final ActionPlanManager actionPlanManager;
 
-  public WebIssuesInternal(ServerIssueActions actions, ActionPlanFinder actionPlanFinder) {
+  public WebIssuesInternal(ServerIssueActions actions, ActionPlanManager actionPlanManager) {
     this.actions = actions;
-    this.actionPlanFinder = actionPlanFinder;
+    this.actionPlanManager = actionPlanManager;
   }
 
   public List<Transition> listTransitions(String issueKey) {
@@ -95,16 +98,85 @@ public class WebIssuesInternal implements ServerComponent {
     return actions.create((DefaultIssue) issue, UserSession.get());
   }
 
-  Collection<ActionPlan> openActionPlans(String projectKey)  {
-    return actionPlanFinder.findOpenByProjectKey(projectKey);
+  Collection<ActionPlan> findOpenActionPlans(String projectKey) {
+    return actionPlanManager.findOpenByProjectKey(projectKey);
   }
 
-  List<ActionPlanStats> openActionPlanStats(String projectKey)  {
-    return actionPlanFinder.findOpenActionPlanStats(projectKey);
+  ActionPlan findActionPlan(String actionPlanKey) {
+    return actionPlanManager.findByKey(actionPlanKey);
   }
 
-  List<ActionPlanStats> closedActionPlanStats(String projectKey)  {
-    return actionPlanFinder.findClosedActionPlanStats(projectKey);
+  List<ActionPlanStats> findOpenActionPlanStats(String projectKey) {
+    return actionPlanManager.findOpenActionPlanStats(projectKey);
+  }
+
+  List<ActionPlanStats> findClosedActionPlanStats(String projectKey) {
+    return actionPlanManager.findClosedActionPlanStats(projectKey);
+  }
+
+  public ActionPlan createActionPlan(Map<String, String> parameters) {
+    // TODO verify authorization
+    // TODO verify deadLine, uniquness of name, ...
+    // TODO check existence of projectId
+    Integer projectId = toInteger(parameters.get("projectId"));
+
+    DefaultActionPlan actionPlan = DefaultActionPlan.create(parameters.get("name"))
+                                     .setDescription(parameters.get("description"))
+                                     .setUserLogin(UserSession.get().login())
+                                     .setDeadLine(toDate(parameters.get("deadLine")));
+
+    return actionPlanManager.create(actionPlan, projectId);
+  }
+
+  public ActionPlan updateActionPlan(String key, Map<String, String> parameters) {
+    // TODO verify authorization
+    // TODO verify deadLine
+    // TODO check existence of projectId
+    Integer projectId = toInteger(parameters.get("projectId"));
+    DefaultActionPlan defaultActionPlan = (DefaultActionPlan) actionPlanManager.findByKey(key);
+    defaultActionPlan.setName(parameters.get("name"));
+    defaultActionPlan.setDescription(parameters.get("description"));
+    defaultActionPlan.setDeadLine(toDate(parameters.get("deadLine")));
+    return actionPlanManager.update(defaultActionPlan, projectId);
+  }
+
+  public ActionPlan closeActionPlan(String actionPlanKey) {
+    // TODO verify authorization
+    return actionPlanManager.setStatus(actionPlanKey, ActionPlan.STATUS_CLOSED);
+  }
+
+  public ActionPlan openActionPlan(String actionPlanKey) {
+    // TODO verify authorization
+    return actionPlanManager.setStatus(actionPlanKey, ActionPlan.STATUS_OPEN);
+  }
+
+  public void deleteActionPlan(String actionPlanKey) {
+    // TODO verify authorization
+    actionPlanManager.delete(actionPlanKey);
+  }
+
+  Date toDate(Object o) {
+    if (o instanceof Date) {
+      return (Date) o;
+    }
+    if (o instanceof String) {
+      return DateUtils.parseDateTime((String) o);
+    }
+    return null;
+  }
+
+  Integer toInteger(Object o) {
+    if (o instanceof Integer) {
+      return (Integer) o;
+    }
+    if (o instanceof Long) {
+      return Ints.checkedCast((Long) o);
+    }
+
+    if (o instanceof String) {
+      return Integer.parseInt((String) o);
+    }
+    return null;
   }
 
 }
index 071d4a531e985fbb67eae7597ae8d8310e54ba08..6a0ba0222e84b5a347373bc3e96cecf4af611f0d 100644 (file)
@@ -40,7 +40,7 @@ import org.sonar.core.config.Logback;
 import org.sonar.core.i18n.GwtI18n;
 import org.sonar.core.i18n.I18nManager;
 import org.sonar.core.i18n.RuleI18nManager;
-import org.sonar.core.issue.ActionPlanFinder;
+import org.sonar.core.issue.ActionPlanManager;
 import org.sonar.core.issue.IssueUpdater;
 import org.sonar.core.issue.workflow.FunctionExecutor;
 import org.sonar.core.issue.workflow.IssueWorkflow;
@@ -260,7 +260,7 @@ public final class Platform {
     servicesContainer.addSingleton(ServerIssueFinder.class);
     servicesContainer.addSingleton(WebIssuesApi.class);
     servicesContainer.addSingleton(WebIssuesInternal.class);
-    servicesContainer.addSingleton(ActionPlanFinder.class);
+    servicesContainer.addSingleton(ActionPlanManager.class);
 
     // rules
     servicesContainer.addSingleton(WebRules.class);
diff --git a/sonar-server/src/main/webapp/WEB-INF/app/controllers/api/action_plans_controller.rb b/sonar-server/src/main/webapp/WEB-INF/app/controllers/api/action_plans_controller.rb
new file mode 100644 (file)
index 0000000..752fcca
--- /dev/null
@@ -0,0 +1,33 @@
+#
+# 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.
+#
+
+class Api::ActionPlansController < Api::ApiController
+
+  #
+  # GET /api/issues/search?<parameters>
+  #
+  # -- Example
+  # curl -v -u admin:admin 'http://localhost:9000/api/issues/search?statuses=OPEN,RESOLVED'
+  #
+  def search
+
+  end
+
+end
\ No newline at end of file
index fd74d9903eadfdac1ec93ce0180e7b46e0eaa894..02fd1955e4983efd1cae98aa02e8dbb0b7c840d5 100644 (file)
@@ -109,7 +109,7 @@ class IssueController < ApplicationController
     require_parameters :issue
     init_issue(params[:issue])
     init_resource
-    @action_plans =  Internal.issues.openActionPlans(@resource.key)
+    @action_plans =  Internal.issues.findOpenActionPlans(@resource.key)
 
     render :partial => 'issue/plan_form'
   end
@@ -206,7 +206,7 @@ class IssueController < ApplicationController
     require_parameters :issue
     init_issue(params[:issue])
     init_resource
-    @action_plans =  Internal.issues.openActionPlans(@resource.key)
+    @action_plans =  Internal.issues.findOpenActionPlans(@resource.key)
 
     render :partial => 'issue/code_viewer/plan_form'
   end
index 8450956786e677018971e1f3260af12881b01797..7111bacf615255a6230abd4d1f3c2d52ae2a88b8 100644 (file)
@@ -29,59 +29,57 @@ class IssuesActionPlansController < ApplicationController
   end
 
   def edit
-    @action_plan = ActionPlan.find_by_key params[:plan_key]
+    @action_plan = find_by_key(params[:plan_key])
     load_action_plans()
     render 'index'
   end
 
   def save
-    @action_plan = ActionPlan.find_by_key params[:plan_key] unless params[:plan_key].blank?
-    unless @action_plan
-      @action_plan = ActionPlan.new(
-          :kee => Java::JavaUtil::UUID.randomUUID().toString(),
-          :user_login => current_user.login,
-          :project_id => @resource.id,
-          :status => ActionPlan::STATUS_OPEN)
-    end
-    
-    @action_plan.name = params[:name]
-    @action_plan.description = params[:description]
+    exiting_action_plan = find_by_key(params[:plan_key]) unless params[:plan_key].blank?
+    options = {'projectId' => @resource.id, 'name' => params[:name], 'description' => params[:description]}
+
     unless params[:deadline].blank?
       begin
         deadline = DateTime.strptime(params[:deadline], '%d/%m/%Y')
         # we check if the date is today or in the future
         if deadline > 1.day.ago
-          @action_plan.deadline = deadline
+          options['deadLine'] = Api::Utils.format_datetime(deadline)
         else
           date_not_valid = message('action_plans.date_cant_be_in_past')
-        end 
+        end
       rescue
         date_not_valid = message('action_plans.date_not_valid')
       end
     end
 
-    if date_not_valid || !@action_plan.valid?
-      @action_plan.errors.add :base, date_not_valid if date_not_valid
-      flash[:error] = @action_plan.errors.full_messages.join('<br/>')
+    if date_not_valid
+      # TODO check errors
+      flash[:error] = date_not_valid
       load_action_plans()
       render 'index'
     else
-      @action_plan.save
+      if exiting_action_plan
+        @action_plan = Internal.issues.updateActionPlan(@action_plan.key, options)
+      else
+        @action_plan = Internal.issues.createActionPlan(options)
+      end
       redirect_to :action => 'index', :id => @resource.id
     end
   end
 
   def delete
-    action_plan = ActionPlan.find_by_key params[:plan_key]
-    action_plan.destroy
+    Internal.issues.deleteActionPlan(params[:plan_key])
     redirect_to :action => 'index', :id => @resource.id
   end
 
   def change_status
-    action_plan = ActionPlan.find_by_key params[:plan_key]
+    action_plan = find_by_key(params[:plan_key])
     if action_plan
-      action_plan.status = action_plan.open? ? ActionPlan::STATUS_CLOSED : ActionPlan::STATUS_OPEN
-      action_plan.save
+      if action_plan.status == 'OPEN'
+        Internal.issues.closeActionPlan(params[:plan_key])
+      else
+        Internal.issues.openActionPlan(params[:plan_key])
+      end
     end
     redirect_to :action => 'index', :id => @resource.id
   end
@@ -93,10 +91,14 @@ class IssuesActionPlansController < ApplicationController
     return redirect_to home_path unless @resource
     access_denied unless has_role?(:admin, @resource)
   end
-  
+
   def load_action_plans
-    @open_action_plans = Internal.issues.openActionPlanStats(@resource.key)
-    @closed_action_plans = Internal.issues.closedActionPlanStats(@resource.key)
+    @open_action_plans = Internal.issues.findOpenActionPlanStats(@resource.key)
+    @closed_action_plans = Internal.issues.findClosedActionPlanStats(@resource.key)
+  end
+
+  def find_by_key(key)
+    Internal.issues.findActionPlan(key)
   end
 
 end
index 39dafeca550acc983df33fdedddff431ad02bb36..8561780241dbf1fc57a9d210c0b093a9814b8323 100644 (file)
@@ -1,23 +1,24 @@
 <table class="admintable" width="100%">
   <form action="<%= url_for :action => 'save' -%>" method="POST" id="create-action-plan-form">
     <input type="hidden" name="id" value="<%= @resource.id -%>"/>
-    <input type="hidden" name="plan_key" value="<%= @action_plan.key if @action_plan -%>"/>
+    <input type="hidden" name="plan_key" value="<%= @action_plan.key() if @action_plan -%>"/>
     <tbody>
       <tr>
-        <td colspan="2"><h1 class="marginbottom10"><%= @action_plan && @action_plan.key ? message('action_plans.edit_action_plan') : message('action_plans.create_new_action_plan') -%></h1></td>
+        <td colspan="2"><h1 class="marginbottom10"><%= @action_plan && @action_plan.key() ? message('action_plans.edit_action_plan') : message('action_plans.create_new_action_plan') -%></h1></td>
       </tr>
       <tr>
         <td class="left" valign="top">
         <%= message('action_plans.col.name') -%>:
         <br/>
-        <input type="text" name="name" id="name" value="<%= @action_plan ? @action_plan.name : params[:name] -%>"/>
+        <input type="text" name="name" id="name" value="<%= @action_plan ? @action_plan.name() : params[:name] -%>"/>
         </td>
       </tr>
       <tr>
         <td class="left" valign="top">
         <%= message('action_plans.col.due_for') -%>:
         <br/>
-        <input type="text" name="deadline" id="deadline" value="<%= @action_plan && @action_plan.deadline ? @action_plan.deadline.strftime('%d/%m/%Y') : params[:deadline] -%>"/>
+        <% deadline = Api::Utils.to_date(plan.deadLine()) if @action_plan && @action_plan.deadLine() %>
+        <input type="text" name="deadline" id="deadline" value="<%= @action_plan && deadline ? deadline.strftime('%d/%m/%Y') : params[:deadline] -%>"/>
         <br/>
         <span class="note"><%= message('action_plans.date_format_help') -%></span>
         </td>
         <td class="left" valign="top">
         <%= message('action_plans.col.description') -%>:
         <br/>
-        <textarea rows="5" cols="80" name="description" id="description" class="width100"><%= @action_plan ? @action_plan.description : params['description'] -%></textarea>
+        <textarea rows="5" cols="80" name="description" id="description" class="width100"><%= @action_plan ? @action_plan.description() : params['description'] -%></textarea>
         </td>
       </tr>
       <tr>
         <td class="left" valign="top">
-        <input type="submit" value="<%= @action_plan && @action_plan.id ? message('action_plans.edit_action_plan') : message('action_plans.create_action_plan') -%>"/>
+        <input type="submit" value="<%= @action_plan && @action_plan.key() ? message('action_plans.edit_action_plan') : message('action_plans.create_action_plan') -%>"/>
         </td>
       </tr>
     </tbody>
index 11e1821c352f0d1e947bde72cdd5be6c3edf7138..2654bf42fcd9ef625e6d2fc38b9a1aaee72d6808 100644 (file)
@@ -31,7 +31,7 @@ import org.sonar.api.issue.IssueQuery;
 import org.sonar.api.rules.Rule;
 import org.sonar.api.web.UserRole;
 import org.sonar.core.component.ComponentDto;
-import org.sonar.core.issue.ActionPlanFinder;
+import org.sonar.core.issue.ActionPlanManager;
 import org.sonar.core.issue.DefaultActionPlan;
 import org.sonar.core.issue.db.IssueDao;
 import org.sonar.core.issue.db.IssueDto;
@@ -61,8 +61,8 @@ public class ServerIssueFinderTest {
   AuthorizationDao authorizationDao = mock(AuthorizationDao.class);
   DefaultRuleFinder ruleFinder = mock(DefaultRuleFinder.class);
   ResourceDao resourceDao = mock(ResourceDao.class);
-  ActionPlanFinder actionPlanFinder = mock(ActionPlanFinder.class);
-  ServerIssueFinder finder = new ServerIssueFinder(mybatis, issueDao, authorizationDao, ruleFinder, resourceDao, actionPlanFinder);
+  ActionPlanManager actionPlanManager = mock(ActionPlanManager.class);
+  ServerIssueFinder finder = new ServerIssueFinder(mybatis, issueDao, authorizationDao, ruleFinder, resourceDao, actionPlanManager);
 
   @Test
   public void should_find_issues() {
@@ -228,7 +228,7 @@ public class ServerIssueFinderTest {
     List<IssueDto> dtoList = newArrayList(issue1, issue2);
     when(issueDao.selectIssueIdsAndComponentsId(eq(query), any(SqlSession.class))).thenReturn(dtoList);
     when(issueDao.selectByIds(anyCollection(), any(SqlSession.class))).thenReturn(dtoList);
-    when(actionPlanFinder.findByKeys(anyCollection())).thenReturn(newArrayList(actionPlan1, actionPlan2));
+    when(actionPlanManager.findByKeys(anyCollection())).thenReturn(newArrayList(actionPlan1, actionPlan2));
 
     IssueFinder.Results results = finder.find(query, null, UserRole.USER);
     assertThat(results.issues()).hasSize(2);