]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-1608 Display all impacts of bulk change in confirmation page
authorFabrice Bellingard <bellingard@gmail.com>
Wed, 4 Jul 2012 13:07:05 +0000 (15:07 +0200)
committerFabrice Bellingard <bellingard@gmail.com>
Wed, 4 Jul 2012 14:03:00 +0000 (16:03 +0200)
+ allow to replace any string of the key, not only the beginning.

plugins/sonar-l10n-en-plugin/src/main/resources/org/sonar/l10n/core.properties
sonar-core/src/main/java/org/sonar/core/resource/ResourceKeyUpdaterDao.java
sonar-core/src/test/java/org/sonar/core/resource/ResourceKeyUpdaterDaoTest.java
sonar-core/src/test/resources/org/sonar/core/resource/ResourceKeyUpdaterDaoTest/shouldBulkUpdateKeyOnOnlyOneSubmodule-result.xml [new file with mode: 0644]
sonar-server/src/main/java/org/sonar/server/ui/JRubyFacade.java
sonar-server/src/main/webapp/WEB-INF/app/controllers/project_controller.rb
sonar-server/src/main/webapp/WEB-INF/app/views/project/_prepare_keys.html.erb [new file with mode: 0644]
sonar-server/src/main/webapp/WEB-INF/app/views/project/key.html.erb
sonar-server/src/main/webapp/WEB-INF/app/views/project/prepare_key_bulk_update.html.erb [new file with mode: 0644]

index 089ea73a2accb250a1033c1dd7102f4c15fe0858..bc17add3c7207cd3051ef615c11ff81a38b3e681 100644 (file)
@@ -969,15 +969,16 @@ update_key.cant_update_x_because_resource_already_exist_with_key_x="{0}" can not
 update_key.error_occured_while_renaming_key_of_x=An error occurred while renaming the key "{0}": {1}
 update_key.key_updated=The key has successfully been updated for all required resources.
 update_key.fieds_cant_be_blank_for_bulk_update=The two fields can not be blank for the bulk update.
-update_key.key_does_not_start_with_x=The current key ("{0}") does not start with "{1}". The update is impossible.
 update_key.bulk_change_description=The bulk update allows to replace the beginning of the current key by another string on the current project and all its submodules - if applicable.
 update_key.current_key_for_project_x_is_x=The key of the "{0}" project is currently "<b>{1}</b>".
 update_key.are_you_sure_to_rename_x=Are you sure you want to rename "{0}", as well as all its modules and resources ?
-update_key.are_you_sure_to_bulk_rename_x_into_x=Are you sure you want to rename "<b>{0}</b>{1}" into "<b>{2}</b>{1}", as well as all its modules and resources ?
 update_key.replace=Replace
 update_key.by=By
 update_key.replace_example=Ex.: "org.myCompany"
 update_key.by_example=Ex.: "com.myNewCompany"
+update_key.cant_update_because_duplicate_keys=The replacement of "{0}" by "{1}" is impossible as it would result in duplicate keys (in red below):
+update_key.keys_will_be_updated_as_follows=The resources will be updated as follows (updated keys in bold): 
+update_key.duplicate_key=Duplicate key
 
 
 #------------------------------------------------------------------------------
index d6d8dbf1a70918b0143025ebc70df5a99e867e1e..e04f0df982cb3b4c81c30a892b3c1a7751b5a871 100644 (file)
  */
 package org.sonar.core.resource;
 
+import com.google.common.collect.Lists;
+import com.google.common.collect.Maps;
 import com.google.common.collect.Sets;
 import org.apache.ibatis.session.SqlSession;
 import org.sonar.core.persistence.MyBatis;
 
 import java.util.Collection;
 import java.util.List;
+import java.util.Map;
 import java.util.Set;
 
 /**
@@ -62,20 +65,46 @@ public class ResourceKeyUpdaterDao {
     }
   }
 
-  public void bulkUpdateKey(long projectId, String oldPrefix, String newPrefix) {
+  public Map<String, String> checkModuleKeysBeforeRenaming(long projectId, String stringToReplace, String replacementString) {
+    SqlSession session = mybatis.openSession();
+    ResourceKeyUpdaterMapper mapper = session.getMapper(ResourceKeyUpdaterMapper.class);
+    Map<String, String> result = Maps.newHashMap();
+    try {
+      Set<ResourceDto> modules = collectAllModules(projectId, stringToReplace, mapper);
+      for (ResourceDto module : modules) {
+        String newKey = computeNewKey(module, stringToReplace, replacementString);
+        if (mapper.countResourceByKey(newKey) > 0) {
+          result.put(module.getKey(), "#duplicate_key#");
+        } else {
+          result.put(module.getKey(), newKey);
+        }
+      }
+    } finally {
+      MyBatis.closeQuietly(session);
+    }
+    return result;
+  }
+
+  public void bulkUpdateKey(long projectId, String stringToReplace, String replacementString) {
     SqlSession session = mybatis.openBatchSession();
     ResourceKeyUpdaterMapper mapper = session.getMapper(ResourceKeyUpdaterMapper.class);
     try {
       // must SELECT first everything
-      Set<ResourceDto> modules = collectAllModules(projectId, oldPrefix, mapper);
-      checkNewNameOfAllModules(modules, oldPrefix, newPrefix, mapper);
-      Set<ResourceDto> allResources = Sets.newHashSet(modules);
+      Set<ResourceDto> modules = collectAllModules(projectId, stringToReplace, mapper);
+      checkNewNameOfAllModules(modules, stringToReplace, replacementString, mapper);
+      Map<ResourceDto, List<ResourceDto>> allResourcesByModuleMap = Maps.newHashMap();
       for (ResourceDto module : modules) {
-        allResources.addAll(mapper.selectProjectResources(module.getId()));
+        allResourcesByModuleMap.put(module, mapper.selectProjectResources(module.getId()));
       }
 
       // and then proceed with the batch UPDATE at once
-      runBatchUpdateForAllResources(allResources, oldPrefix, newPrefix, mapper);
+      for (ResourceDto module : modules) {
+        String oldModuleKey = module.getKey();
+        String newModuleKey = computeNewKey(module, stringToReplace, replacementString);
+        Collection<ResourceDto> resources = Lists.newArrayList(module);
+        resources.addAll(allResourcesByModuleMap.get(module));
+        runBatchUpdateForAllResources(resources, oldModuleKey, newModuleKey, mapper);
+      }
 
       session.commit();
     } finally {
@@ -83,33 +112,33 @@ public class ResourceKeyUpdaterDao {
     }
   }
 
-  private String computeNewKey(ResourceDto resource, String oldPrefix, String newPrefix) {
-    String resourceKey = resource.getKey();
-    return newPrefix + resourceKey.substring(oldPrefix.length(), resourceKey.length());
+  private String computeNewKey(ResourceDto resource, String stringToReplace, String replacementString) {
+    return resource.getKey().replaceAll(stringToReplace, replacementString);
   }
 
-  private void runBatchUpdateForAllResources(Collection<ResourceDto> resources, String oldPrefix, String newPrefix, ResourceKeyUpdaterMapper mapper) {
+  private void runBatchUpdateForAllResources(Collection<ResourceDto> resources, String oldKey, String newKey, ResourceKeyUpdaterMapper mapper) {
     for (ResourceDto resource : resources) {
-      resource.setKey(computeNewKey(resource, oldPrefix, newPrefix));
+      String resourceKey = resource.getKey();
+      resource.setKey(newKey + resourceKey.substring(oldKey.length(), resourceKey.length()));
       mapper.update(resource);
     }
   }
 
-  private Set<ResourceDto> collectAllModules(long projectId, String oldPrefix, ResourceKeyUpdaterMapper mapper) {
+  private Set<ResourceDto> collectAllModules(long projectId, String stringToReplace, ResourceKeyUpdaterMapper mapper) {
     ResourceDto project = mapper.selectProject(projectId);
     Set<ResourceDto> modules = Sets.newHashSet();
-    if (project.getKey().startsWith(oldPrefix)) {
+    if (project.getKey().contains(stringToReplace)) {
       modules.add(project);
     }
     for (ResourceDto submodule : mapper.selectDescendantProjects(projectId)) {
-      modules.addAll(collectAllModules(submodule.getId(), oldPrefix, mapper));
+      modules.addAll(collectAllModules(submodule.getId(), stringToReplace, mapper));
     }
     return modules;
   }
 
-  private void checkNewNameOfAllModules(Set<ResourceDto> modules, String oldPrefix, String newPrefix, ResourceKeyUpdaterMapper mapper) {
+  private void checkNewNameOfAllModules(Set<ResourceDto> modules, String stringToReplace, String replacementString, ResourceKeyUpdaterMapper mapper) {
     for (ResourceDto module : modules) {
-      String newName = computeNewKey(module, oldPrefix, newPrefix);
+      String newName = computeNewKey(module, stringToReplace, replacementString);
       if (mapper.countResourceByKey(newName) > 0) {
         throw new IllegalStateException("Impossible to update key: a resource with \"" + newName + "\" key already exists.");
       }
index bfa64d86dc47e463c659bd970271a8e47c23f832..d1688d27a378120c63dff9741b1f3d88e996563b 100644 (file)
@@ -25,6 +25,10 @@ import org.junit.Test;
 import org.junit.rules.ExpectedException;
 import org.sonar.core.persistence.DaoTestCase;
 
+import java.util.Map;
+
+import static org.fest.assertions.Assertions.assertThat;
+
 public class ResourceKeyUpdaterDaoTest extends DaoTestCase {
 
   @Rule
@@ -65,6 +69,15 @@ public class ResourceKeyUpdaterDaoTest extends DaoTestCase {
     checkTables("shouldBulkUpdateKey", "projects");
   }
 
+  @Test
+  public void shouldBulkUpdateKeyOnOnlyOneSubmodule() {
+    setupData("shared");
+
+    dao.bulkUpdateKey(1, "struts-ui", "struts-web");
+
+    checkTables("shouldBulkUpdateKeyOnOnlyOneSubmodule", "projects");
+  }
+
   @Test
   public void shouldFailBulkUpdateKeyIfKeyAlreadyExist() {
     setupData("shared");
@@ -84,4 +97,15 @@ public class ResourceKeyUpdaterDaoTest extends DaoTestCase {
     checkTables("shouldNotUpdateAllSubmodules", "projects");
   }
 
+  @Test
+  public void shouldCheckModuleKeysBeforeRenaming() {
+    setupData("shared");
+
+    Map<String, String> checkResults = dao.checkModuleKeysBeforeRenaming(1, "org.struts", "foo");
+    assertThat(checkResults.size()).isEqualTo(3);
+    assertThat(checkResults.get("org.struts:struts")).isEqualTo("foo:struts");
+    assertThat(checkResults.get("org.struts:struts-core")).isEqualTo("update_impossible");
+    assertThat(checkResults.get("org.struts:struts-ui")).isEqualTo("foo:struts-ui");
+  }
+
 }
diff --git a/sonar-core/src/test/resources/org/sonar/core/resource/ResourceKeyUpdaterDaoTest/shouldBulkUpdateKeyOnOnlyOneSubmodule-result.xml b/sonar-core/src/test/resources/org/sonar/core/resource/ResourceKeyUpdaterDaoTest/shouldBulkUpdateKeyOnOnlyOneSubmodule-result.xml
new file mode 100644 (file)
index 0000000..461018f
--- /dev/null
@@ -0,0 +1,50 @@
+<dataset>
+
+  <!-- root project -->
+  <projects id="1" root_id="[null]" scope="PRJ" qualifier="TRK" kee="org.struts:struts" name="Struts"
+            description="[null]" long_name="Apache Struts" profile_id="1"
+            enabled="[true]" language="java" copy_resource_id="[null]" person_id="[null]"/>
+
+
+  <!-- **************** First sub project **************** -->
+  <projects id="2" root_id="1" kee="org.struts:struts-core" name="Struts Core"
+            scope="PRJ" qualifier="BRC" long_name="Struts Core" profile_id="1"
+            description="[null]" enabled="[true]" language="java" copy_resource_id="[null]" person_id="[null]"/>
+
+  <!-- directory -->
+  <projects long_name="org.struts" id="3" scope="DIR" qualifier="PAC" kee="org.struts:struts-core:org.struts"
+              name="org.struts" root_id="2"
+              description="[null]" profile_id="1"
+              enabled="[true]" language="java" copy_resource_id="[null]" person_id="[null]"/>
+
+  <!-- file -->
+  <projects long_name="org.struts.RequestContext" id="4" scope="FIL" qualifier="CLA" kee="org.struts:struts-core:org.struts.RequestContext"
+            name="RequestContext" root_id="2"
+            description="[null]" profile_id="1"
+            enabled="[true]" language="java" copy_resource_id="[null]" person_id="[null]"/>
+
+
+  <!-- **************** Second sub project **************** -->
+  <projects id="5" root_id="1" kee="org.struts:struts-web" name="Struts UI"
+            scope="PRJ" qualifier="BRC" long_name="Struts UI" profile_id="1"
+            description="[null]" enabled="[true]" language="java" copy_resource_id="[null]" person_id="[null]"/>
+
+  <!-- directory -->
+  <projects long_name="org.struts" id="6" scope="DIR" qualifier="PAC" kee="org.struts:struts-web:org.struts"
+              name="org.struts" root_id="5"
+              description="[null]" profile_id="1"
+              enabled="[true]" language="java" copy_resource_id="[null]" person_id="[null]"/>
+
+  <!-- file -->
+  <projects long_name="org.struts.RequestContext" id="7" scope="FIL" qualifier="CLA" kee="org.struts:struts-web:org.struts.RequestContext"
+            name="RequestContext" root_id="5"
+            description="[null]" profile_id="1"
+            enabled="[true]" language="java" copy_resource_id="[null]" person_id="[null]"/>
+
+
+  <!-- **************** Another independent project **************** -->
+  <projects id="8" root_id="[null]" kee="foo:struts-core" name="Foo Struts Core"
+            scope="PRJ" qualifier="BRC" long_name="Foo Struts Core" profile_id="1"
+            description="[null]" enabled="[true]" language="java" copy_resource_id="[null]" person_id="[null]"/>
+
+</dataset>
index f930f742a0a47216cc426c76c32b3de3d37a195b..76ba04b7168d1812afd0e4c55c6f6da62af90113 100644 (file)
@@ -516,8 +516,12 @@ public final class JRubyFacade {
     getContainer().getComponentByType(ResourceKeyUpdaterDao.class).updateKey(projectId, newKey);
   }
 
-  public void bulkUpdateKey(long projectId, String oldPrefix, String newPrefix) {
-    getContainer().getComponentByType(ResourceKeyUpdaterDao.class).bulkUpdateKey(projectId, oldPrefix, newPrefix);
+  public Map<String, String> checkModuleKeysBeforeRenaming(long projectId, String stringToReplace, String replacementString) {
+    return getContainer().getComponentByType(ResourceKeyUpdaterDao.class).checkModuleKeysBeforeRenaming(projectId, stringToReplace, replacementString);
+  }
+
+  public void bulkUpdateKey(long projectId, String stringToReplace, String replacementString) {
+    getContainer().getComponentByType(ResourceKeyUpdaterDao.class).bulkUpdateKey(projectId, stringToReplace, replacementString);
   }
 
 }
index b03aebea15899958e9dd75292e025cdd07e12f92..ba9e1eb1059777643df277c536e084f55ae96089 100644 (file)
@@ -71,11 +71,6 @@ class ProjectController < ApplicationController
   
   def key
     @project = get_current_project(params[:id])
-      
-    if params[:old_prefix] && params[:new_prefix]
-      @old_prefix = params[:old_prefix]
-      @new_prefix = params[:new_prefix]
-    end
   end
   
   def update_key
@@ -102,27 +97,20 @@ class ProjectController < ApplicationController
   end
   
   def prepare_key_bulk_update
-    project = get_current_project(params[:id])
+    @project = get_current_project(params[:id])
     
-    old_prefix = params[:old_prefix].strip
-    new_prefix = params[:new_prefix].strip
-    if old_prefix.blank? || new_prefix.blank?
-      error_message = message('update_key.fieds_cant_be_blank_for_bulk_update')
-    elsif !project.key.start_with?(old_prefix)
-      error_message = message('update_key.key_does_not_start_with_x', :params => [project.key, old_prefix])
+    @old_prefix = params[:old_prefix].strip
+    @new_prefix = params[:new_prefix].strip
+    if @old_prefix.blank? || @new_prefix.blank?
+      flash[:error] = message('update_key.fieds_cant_be_blank_for_bulk_update')
+      redirect_to :action => 'key', :id => @project.id
     else
-      new_key = new_prefix + project.key[old_prefix.size..project.key.size]
-      if Project.by_key(new_key)
-        error_message = message('update_key.cant_update_x_because_resource_already_exist_with_key_x', :params => [project.key, new_key])  
+      @key_check_results = java_facade.checkModuleKeysBeforeRenaming(@project.id, @old_prefix, @new_prefix)
+      @can_update = true
+      @key_check_results.each do |key, value|
+        @can_update = false if value=="#duplicate_key#"
       end
     end
-    
-    if error_message
-      flash[:error] = error_message
-      redirect_to :action => 'key', :id => project.id
-    else
-      redirect_to :action => 'key', :id => project.id, :old_prefix => old_prefix, :new_prefix => new_prefix
-    end
   end
 
   def perform_key_bulk_update
@@ -131,7 +119,7 @@ class ProjectController < ApplicationController
     old_prefix = params[:old_prefix].strip
     new_prefix = params[:new_prefix].strip
       
-    unless old_prefix.blank? || new_prefix.blank? || !project.key.start_with?(old_prefix)
+    unless old_prefix.blank? || new_prefix.blank?
       begin
         java_facade.bulkUpdateKey(project.id, old_prefix, new_prefix)
         flash[:notice] = message('update_key.key_updated')
diff --git a/sonar-server/src/main/webapp/WEB-INF/app/views/project/_prepare_keys.html.erb b/sonar-server/src/main/webapp/WEB-INF/app/views/project/_prepare_keys.html.erb
new file mode 100644 (file)
index 0000000..1624108
--- /dev/null
@@ -0,0 +1,19 @@
+<%
+  future_key = key_check_results[current_module.key]
+  if future_key=="#duplicate_key#"
+    duplicate_key = true
+    future_key = message('update_key.duplicate_key')
+  end
+%>
+    <tr class="<%= cycle 'even', 'odd', :name => 'modules_tree' -%>">
+      <td class="thin nowrap" style="padding-left: <%= 3+ module_depth*15 -%>px;">
+        <%= h(current_module.key) -%>
+      </td>
+      <td class="thin nowrap <%= 'error' if duplicate_key -%>" style="<%= 'font-weight: bold;' if future_key -%>">
+        <%= future_key ? future_key : current_module.key -%>
+      </td>
+    </tr>
+    <% current_module.modules.each_with_index do |sub_module, index| %>
+      <%= render :partial => 'prepare_keys', :locals => {:current_module => sub_module, :module_depth => module_depth+1, 
+                                                        :key_check_results => key_check_results} -%>
+    <% end %>
\ No newline at end of file
index e28d9f59481ea526438edbbc195f649b0e878807..aa2bd82a76391b8c32d7af98ed7f6c2df01eed63 100644 (file)
@@ -1,6 +1,7 @@
 <% 
   if controller.java_facade.getResourceTypeBooleanProperty(@project.qualifier, 'updatable_key')
     has_modules = !@project.modules.empty?
+    reset_cycle 'modules_tree'
 %>
 
   <script type="text/javascript">
     }
   </script>
 
-<%    
-    if @old_prefix && @new_prefix
-      # validation screen for bulk update
-      key_suffix = @project.key[@old_prefix.size..@project.key.size]
-%>  
-  
-  <h1><%= message('update_key.bulk_update') -%></h1>
-  <br/>
-  <p>
-    <%= message('update_key.are_you_sure_to_bulk_rename_x_into_x', :params => [@old_prefix, key_suffix, @new_prefix]) -%>
-  </p>
-  <% form_tag( {:action => 'perform_key_bulk_update', :id => @project.id }) do -%>
-    <input type="hidden" value="<%= @project.id -%>" name="id" id="project_id">
-    <input type="hidden" value="<%= @old_prefix -%>" name="old_prefix" id="old_prefix">
-    <input type="hidden" value="<%= @new_prefix -%>" name="new_prefix" id="new_prefix">
-    <br/>
-    <%= submit_tag message('update_key.rename'), :id => 'bulk_update_button', :class => 'action',
-                         :onclick => "update_launched();$('loading_bulk_update').show();" %>
-    <a href="<%= url_for :action => 'key', :id => @project.key -%>"><%= message('cancel') -%></a>
-    <span class="loading" id="loading_bulk_update" style="display: none; padding: 3px 10px;"></span>
-  <% end %>
-    
-<%
-    else
-      # main screen
-      reset_cycle 'modules_tree'
-%>
-
 <% if has_modules %>
   <h1><%= message('update_key.bulk_update') -%></h1>
   <br/>
@@ -77,7 +50,7 @@
 
   <h1><%= has_modules ? message('update_key.fine_grained_key_update') : message('update_key.update_resource_key') -%></h1>
   <br/>
-  <table id="project-history" class="data" style="width:1%">
+  <table class="data" style="width:1%">
     <thead>
       <tr>
         <th><%= message('update_key.old_key') -%></th>
@@ -89,7 +62,4 @@
     </tbody>
   </table>
   
-<% 
-    end
-  end 
-%>
\ No newline at end of file
+<% end %>
\ No newline at end of file
diff --git a/sonar-server/src/main/webapp/WEB-INF/app/views/project/prepare_key_bulk_update.html.erb b/sonar-server/src/main/webapp/WEB-INF/app/views/project/prepare_key_bulk_update.html.erb
new file mode 100644 (file)
index 0000000..7d03a1a
--- /dev/null
@@ -0,0 +1,49 @@
+<%    
+    if @old_prefix && @new_prefix
+      # validation screen for bulk update
+      reset_cycle 'modules_tree'
+%>
+
+  <script type="text/javascript">
+    function update_launched() {
+      $$('input.action').each(function(input) {
+        input.disabled=true;
+      });
+    }
+  </script>  
+  
+  <h1><%= message('update_key.bulk_update') -%></h1>
+  <br/>
+  <p>
+    <%= @can_update ? message('update_key.keys_will_be_updated_as_follows') : message('update_key.cant_update_because_duplicate_keys', :params => [@old_prefix, @new_prefix]) -%>
+  </p>
+  
+  <table class="data" style="width:1%; margin-top: 10px">
+    <thead>
+      <tr>
+        <th><%= message('update_key.old_key') -%></th>
+        <th><%= message('update_key.new_key') -%></th>
+      </tr>
+    </thead>
+    <tbody>
+      <%= render :partial => 'prepare_keys', :locals => {:current_module => @project, :module_depth => 0, :key_check_results => @key_check_results} -%>
+    </tbody>
+  </table>
+  
+  <% if @can_update %>
+    <% form_tag( {:action => 'perform_key_bulk_update', :id => @project.id }) do -%>
+      <input type="hidden" value="<%= @project.id -%>" name="id" id="project_id">
+      <input type="hidden" value="<%= @old_prefix -%>" name="old_prefix" id="old_prefix">
+      <input type="hidden" value="<%= @new_prefix -%>" name="new_prefix" id="new_prefix">
+      <br/>
+      <%= submit_tag message('update_key.rename'), :id => 'bulk_update_button', :class => 'action',
+                             :onclick => "update_launched();$('loading_bulk_update').show();" %>
+      <a href="<%= url_for :action => 'key', :id => @project.key -%>"><%= message('cancel') -%></a>
+      <span class="loading" id="loading_bulk_update" style="display: none; padding: 3px 10px;"></span>
+    <% end %>
+  <% else %>
+    <br/>
+    <a href="<%= url_for :action => 'key', :id => @project.key -%>"><%= message('back') -%></a>
+  <% end %>
+    
+<% end %>
\ No newline at end of file