]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-3646 API : new extension point to be notified on user creation
authorSimon Brandhof <simon.brandhof@gmail.com>
Wed, 11 Jul 2012 18:29:18 +0000 (20:29 +0200)
committerSimon Brandhof <simon.brandhof@gmail.com>
Wed, 11 Jul 2012 18:29:35 +0000 (20:29 +0200)
sonar-plugin-api/src/main/java/org/sonar/api/platform/NewUserHandler.java [new file with mode: 0644]
sonar-plugin-api/src/test/java/org/sonar/api/platform/NewUserHandlerTest.java [new file with mode: 0644]
sonar-server/src/main/java/org/sonar/server/platform/NewUserNotifier.java [new file with mode: 0644]
sonar-server/src/main/java/org/sonar/server/platform/Platform.java
sonar-server/src/main/java/org/sonar/server/ui/JRubyFacade.java
sonar-server/src/main/webapp/WEB-INF/app/controllers/users_controller.rb
sonar-server/src/main/webapp/WEB-INF/app/models/user.rb
sonar-server/src/test/java/org/sonar/server/platform/NewUserNotifierTest.java [new file with mode: 0644]

diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/platform/NewUserHandler.java b/sonar-plugin-api/src/main/java/org/sonar/api/platform/NewUserHandler.java
new file mode 100644 (file)
index 0000000..833c68d
--- /dev/null
@@ -0,0 +1,91 @@
+/*
+ * Sonar, open source software quality management tool.
+ * Copyright (C) 2008-2012 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * Sonar 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.
+ *
+ * Sonar 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 Sonar; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02
+ */
+package org.sonar.api.platform;
+
+import com.google.common.base.Preconditions;
+import org.sonar.api.ServerExtension;
+
+import javax.annotation.Nullable;
+
+/**
+ * @since 3.2
+ */
+public interface NewUserHandler extends ServerExtension {
+
+  public static final class Context {
+    private String login;
+    private String name;
+    private String email;
+
+    private Context(String login, String name, @Nullable String email) {
+      Preconditions.checkNotNull(login);
+      Preconditions.checkNotNull(name);
+      this.login = login;
+      this.name = name;
+      this.email = email;
+    }
+
+    public String getLogin() {
+      return login;
+    }
+
+    public String getName() {
+      return name;
+    }
+
+    public String getEmail() {
+      return email;
+    }
+
+    public static Builder builder() {
+      return new Builder();
+    }
+
+    public static final class Builder {
+      private String login;
+      private String name;
+      private String email;
+
+      private Builder() {
+      }
+
+      public Builder setLogin(String s) {
+        this.login = s;
+        return this;
+      }
+
+      public Builder setName(String s) {
+        this.name = s;
+        return this;
+      }
+
+      public Builder setEmail(@Nullable String s) {
+        this.email = s;
+        return this;
+      }
+
+      public Context build() {
+        return new Context(login, name, email);
+      }
+    }
+  }
+
+  void doOnNewUser(Context context);
+}
diff --git a/sonar-plugin-api/src/test/java/org/sonar/api/platform/NewUserHandlerTest.java b/sonar-plugin-api/src/test/java/org/sonar/api/platform/NewUserHandlerTest.java
new file mode 100644 (file)
index 0000000..e5cc3a4
--- /dev/null
@@ -0,0 +1,48 @@
+/*
+ * Sonar, open source software quality management tool.
+ * Copyright (C) 2008-2012 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * Sonar 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.
+ *
+ * Sonar 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 Sonar; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02
+ */
+package org.sonar.api.platform;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+
+import static org.fest.assertions.Assertions.assertThat;
+
+public class NewUserHandlerTest {
+
+  @Rule
+  public ExpectedException thrown = ExpectedException.none();
+
+  @Test
+  public void build_context() {
+    NewUserHandler.Context context = NewUserHandler.Context.builder().setLogin("marius").setName("Marius").setEmail("marius@lesbronzes.fr").build();
+
+    assertThat(context.getLogin()).isEqualTo("marius");
+    assertThat(context.getName()).isEqualTo("Marius");
+    assertThat(context.getEmail()).isEqualTo("marius@lesbronzes.fr");
+  }
+
+  @Test
+  public void login_is_mandatory() {
+    thrown.expect(NullPointerException.class);
+
+    NewUserHandler.Context.builder().setName("Marius").build();
+  }
+}
diff --git a/sonar-server/src/main/java/org/sonar/server/platform/NewUserNotifier.java b/sonar-server/src/main/java/org/sonar/server/platform/NewUserNotifier.java
new file mode 100644 (file)
index 0000000..a6f1d22
--- /dev/null
@@ -0,0 +1,47 @@
+/*
+ * Sonar, open source software quality management tool.
+ * Copyright (C) 2008-2012 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * Sonar 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.
+ *
+ * Sonar 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 Sonar; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02
+ */
+package org.sonar.server.platform;
+
+import org.slf4j.LoggerFactory;
+import org.sonar.api.ServerComponent;
+import org.sonar.api.platform.NewUserHandler;
+
+/**
+ * @since 3.2
+ */
+public class NewUserNotifier implements ServerComponent {
+
+  private NewUserHandler[] handlers;
+
+  public NewUserNotifier(NewUserHandler[] handlers) {
+    this.handlers = handlers;
+  }
+
+  public NewUserNotifier() {
+    this(new NewUserHandler[0]);
+  }
+
+  public void onNewUser(NewUserHandler.Context context) {
+    LoggerFactory.getLogger(NewUserNotifier.class).debug("User created: " + context.getLogin() + ". Notifying " + NewUserHandler.class.getSimpleName() + " handlers...");
+    for (NewUserHandler handler : handlers) {
+      handler.doOnNewUser(context);
+    }
+  }
+}
\ No newline at end of file
index 2ce18b2e8814444b589ba89f93c6a1c36e98ba82..e20fed0c4c0dc415d6f0e07bb0f9e0bdc27acadb 100644 (file)
@@ -241,6 +241,7 @@ public final class Platform {
     servicesContainer.addSingleton(RuleI18nManager.class);
     servicesContainer.addSingleton(GwtI18n.class);
     servicesContainer.addSingleton(ResourceTypes.class);
+    servicesContainer.addSingleton(NewUserNotifier.class);
 
     // Notifications
     servicesContainer.addSingleton(EmailSettings.class);
index 76ba04b7168d1812afd0e4c55c6f6da62af90113..d2f4a2bb4496903a97b9c419c0d21d1b3a736729 100644 (file)
@@ -27,6 +27,7 @@ import org.sonar.api.config.License;
 import org.sonar.api.config.PropertyDefinitions;
 import org.sonar.api.config.Settings;
 import org.sonar.api.platform.ComponentContainer;
+import org.sonar.api.platform.NewUserHandler;
 import org.sonar.api.platform.PluginMetadata;
 import org.sonar.api.platform.PluginRepository;
 import org.sonar.api.profiles.ProfileExporter;
@@ -37,11 +38,7 @@ import org.sonar.api.resources.ResourceTypes;
 import org.sonar.api.rules.RulePriority;
 import org.sonar.api.rules.RuleRepository;
 import org.sonar.api.utils.ValidationMessages;
-import org.sonar.api.web.Footer;
-import org.sonar.api.web.NavigationSection;
-import org.sonar.api.web.Page;
-import org.sonar.api.web.RubyRailsWebservice;
-import org.sonar.api.web.Widget;
+import org.sonar.api.web.*;
 import org.sonar.api.workflow.Review;
 import org.sonar.api.workflow.internal.DefaultReview;
 import org.sonar.api.workflow.internal.DefaultWorkflowContext;
@@ -61,19 +58,15 @@ import org.sonar.server.filters.FilterExecutor;
 import org.sonar.server.filters.FilterResult;
 import org.sonar.server.notifications.reviews.ReviewsNotificationManager;
 import org.sonar.server.platform.GlobalSettingsUpdater;
+import org.sonar.server.platform.NewUserNotifier;
 import org.sonar.server.platform.Platform;
 import org.sonar.server.platform.ServerIdGenerator;
-import org.sonar.server.plugins.DefaultServerPluginRepository;
-import org.sonar.server.plugins.PluginDeployer;
-import org.sonar.server.plugins.PluginDownloader;
-import org.sonar.server.plugins.UpdateCenterMatrix;
-import org.sonar.server.plugins.UpdateCenterMatrixFactory;
+import org.sonar.server.plugins.*;
 import org.sonar.server.rules.ProfilesConsole;
 import org.sonar.server.rules.RulesConsole;
 import org.sonar.updatecenter.common.Version;
 
 import javax.annotation.Nullable;
-
 import java.net.InetAddress;
 import java.sql.Connection;
 import java.util.Collection;
@@ -319,7 +312,7 @@ public final class JRubyFacade {
 
   public void ruleSeverityChanged(int parentProfileId, int activeRuleId, int oldSeverityId, int newSeverityId, String userName) {
     getProfilesManager().ruleSeverityChanged(parentProfileId, activeRuleId, RulePriority.values()[oldSeverityId],
-        RulePriority.values()[newSeverityId], userName);
+      RulePriority.values()[newSeverityId], userName);
   }
 
   public void ruleDeactivated(int parentProfileId, int deactivatedRuleId, String userName) {
@@ -524,4 +517,13 @@ public final class JRubyFacade {
     getContainer().getComponentByType(ResourceKeyUpdaterDao.class).bulkUpdateKey(projectId, stringToReplace, replacementString);
   }
 
+
+  // USERS
+  public void onNewUser(Map<String, String> fields) {
+    getContainer().getComponentByType(NewUserNotifier.class).onNewUser(NewUserHandler.Context.builder()
+      .setLogin(fields.get("login"))
+      .setName(fields.get("name"))
+      .setEmail(fields.get("email"))
+      .build());
+  }
 }
index b238a0bf7097068c8a218c9bbd128e17b185015c..a187754727c4ce1ce4c49e744111b29d3eb823ee 100644 (file)
@@ -116,8 +116,7 @@ class UsersController < ApplicationController
   def reactivate
     user = User.find_by_login(params[:user][:login])
     if user
-      user.reactivate(java_facade.getSettings().getString('sonar.defaultGroup'))
-      user.save!
+      user.reactivate!(java_facade.getSettings().getString('sonar.defaultGroup'))
       flash[:notice] = 'User was successfully reactivated.'
     else
       flash[:error] = "A user with login #{params[:user][:login]} does not exist."
index 2e25ac4dad735136ed115b93502c66b72d588a83..c70d33985a4cc50f3945d9f64b08ff4d6c928bd9 100644 (file)
@@ -23,6 +23,7 @@ class User < ActiveRecord::Base
 
   FAVOURITE_PROPERTY_KEY='favourite'
 
+  after_create :on_create
   has_and_belongs_to_many :groups
 
   has_many :user_roles, :dependent => :delete_all
@@ -37,17 +38,17 @@ class User < ActiveRecord::Base
   include NeedAuthorization::ForUser
   include NeedAuthentication::ForUser
 
-  validates_length_of       :name,  :maximum => 200, :allow_blank => true, :allow_nil => true
-  validates_length_of       :email, :maximum => 100, :allow_blank => true, :allow_nil => true
+  validates_length_of :name, :maximum => 200, :allow_blank => true, :allow_nil => true
+  validates_length_of :email, :maximum => 100, :allow_blank => true, :allow_nil => true
 
   # The following two validations not needed, because they come with Authentication::ByPassword - see SONAR-2656
   #validates_length_of       :password, :within => 4..40, :if => :password_required?
   #validates_confirmation_of :password, :if => :password_required?
 
-  validates_presence_of     :login
-  validates_length_of       :login,    :within => 2..40
-  validates_uniqueness_of   :login,    :case_sensitive => true
-  validates_format_of       :login,    :with => Authentication.login_regex, :message => Authentication.bad_login_message
+  validates_presence_of :login
+  validates_length_of :login, :within => 2..40
+  validates_uniqueness_of :login, :case_sensitive => true
+  validates_format_of :login, :with => Authentication.login_regex, :message => Authentication.bad_login_message
 
 
   # HACK HACK HACK -- how to do attr_accessible from here?
@@ -90,20 +91,22 @@ class User < ActiveRecord::Base
 
     # do not validate user, for example when user created via SSO has no password
     self.save(false)
-    self.user_roles.each {|role| role.delete}
-    self.properties.each {|prop| prop.delete}
-    self.filters.each {|f| f.destroy}
-    self.dashboards.each {|d| d.destroy}
-    self.active_dashboards.each {|ad| ad.destroy}
+    self.user_roles.each { |role| role.delete }
+    self.properties.each { |prop| prop.delete }
+    self.filters.each { |f| f.destroy }
+    self.dashboards.each { |d| d.destroy }
+    self.active_dashboards.each { |ad| ad.destroy }
   end
 
   # SONAR-3258
-  def reactivate(default_group_name)
+  def reactivate!(default_group_name)
     if default_group_name
       default_group=Group.find_by_name(default_group_name)
       self.groups<<default_group if default_group
     end
     self.active = true
+    save!
+    on_create
   end
 
   def self.find_active_by_login(login)
@@ -149,7 +152,7 @@ class User < ActiveRecord::Base
 
   def self.logins_to_ids(logins=[])
     if logins.size>0
-      User.find(:all, :select => 'id', :conditions => ['login in (?)', logins]).map{|user| user.id}
+      User.find(:all, :select => 'id', :conditions => ['login in (?)', logins]).map { |user| user.id }
     else
       []
     end
@@ -162,7 +165,7 @@ class User < ActiveRecord::Base
   def favourite_ids
     @favourite_ids ||=
       begin
-        properties().select{|p| p.key==FAVOURITE_PROPERTY_KEY}.map{|p| p.resource_id}
+        properties().select { |p| p.key==FAVOURITE_PROPERTY_KEY }.map { |p| p.resource_id }
       end
     @favourite_ids
   end
@@ -187,7 +190,7 @@ class User < ActiveRecord::Base
       rid = resource.id if resource
     end
     if rid
-      props=properties().select{|p| p.key==FAVOURITE_PROPERTY_KEY && p.resource_id==rid}
+      props=properties().select { |p| p.key==FAVOURITE_PROPERTY_KEY && p.resource_id==rid }
       if props.size>0
         properties().delete(props)
         return true
@@ -199,4 +202,11 @@ class User < ActiveRecord::Base
   def favourite?(resource_id)
     favourite_ids().include?(resource_id.to_i)
   end
+
+
+  private
+
+  def on_create
+    Java::OrgSonarServerUi::JRubyFacade.getInstance().onNewUser({'login' => self.login, 'name' => self.name, 'email' => self.email})
+  end
 end
diff --git a/sonar-server/src/test/java/org/sonar/server/platform/NewUserNotifierTest.java b/sonar-server/src/test/java/org/sonar/server/platform/NewUserNotifierTest.java
new file mode 100644 (file)
index 0000000..62618af
--- /dev/null
@@ -0,0 +1,51 @@
+/*
+ * Sonar, open source software quality management tool.
+ * Copyright (C) 2008-2012 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * Sonar 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.
+ *
+ * Sonar 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 Sonar; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02
+ */
+package org.sonar.server.platform;
+
+import org.junit.Test;
+import org.sonar.api.platform.NewUserHandler;
+
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+
+public class NewUserNotifierTest {
+
+  private NewUserHandler.Context context = NewUserHandler.Context.builder().setLogin("marius").setName("Marius").build();
+
+  @Test
+  public void do_not_fail_if_no_handlers() {
+    NewUserNotifier notifier = new NewUserNotifier();
+
+    notifier.onNewUser(context);
+  }
+
+  @Test
+  public void execute_handlers_on_new_user() {
+    NewUserHandler handler1 = mock(NewUserHandler.class);
+    NewUserHandler handler2 = mock(NewUserHandler.class);
+    NewUserNotifier notifier = new NewUserNotifier(new NewUserHandler[]{handler1, handler2});
+
+
+    notifier.onNewUser(context);
+
+    verify(handler1).doOnNewUser(context);
+    verify(handler2).doOnNewUser(context);
+  }
+}