diff --git a/app/controllers/admin/groups_controller.rb b/app/controllers/admin/groups_controller.rb index 5de992cee60..6f4df60047f 100644 --- a/app/controllers/admin/groups_controller.rb +++ b/app/controllers/admin/groups_controller.rb @@ -55,7 +55,10 @@ class Admin::GroupsController < Admin::AdminController # group rename is ignored for automatic groups group.name = group_params[:name] if group_params[:name] && !group.automatic - save_group(group) { |g| GroupActionLogger.new(current_user, g).log_change_group_settings } + save_group(group) do |group| + GroupActionLogger.new(current_user, group).log_change_group_settings + DiscourseEvent.trigger(:group_updated, group) + end end def save_group(group) diff --git a/app/controllers/categories_controller.rb b/app/controllers/categories_controller.rb index aaaf1ec7bd6..e2fa98fda1a 100644 --- a/app/controllers/categories_controller.rb +++ b/app/controllers/categories_controller.rb @@ -149,6 +149,7 @@ class CategoriesController < ApplicationController old_permissions = cat.permissions_params if result = cat.update(category_params) + DiscourseEvent.trigger(:category_updated, cat) Scheduler::Defer.later "Log staff action change category settings" do @staff_action_logger.log_category_settings_change(@category, category_params, old_permissions) end @@ -165,6 +166,7 @@ class CategoriesController < ApplicationController custom_slug = params[:slug].to_s if custom_slug.present? && @category.update_attributes(slug: custom_slug) + DiscourseEvent.trigger(:category_updated, @category) render json: success_json else render_json_error(@category) diff --git a/app/controllers/groups_controller.rb b/app/controllers/groups_controller.rb index 8233d5ab99e..6d370477456 100644 --- a/app/controllers/groups_controller.rb +++ b/app/controllers/groups_controller.rb @@ -137,6 +137,7 @@ class GroupsController < ApplicationController if group.update_attributes(group_params) GroupActionLogger.new(current_user, group).log_change_group_settings + DiscourseEvent.trigger(:group_updated, group) render json: success_json else diff --git a/app/jobs/regular/emit_web_hook_event.rb b/app/jobs/regular/emit_web_hook_event.rb index cc80fcebb2f..a893b49689a 100644 --- a/app/jobs/regular/emit_web_hook_event.rb +++ b/app/jobs/regular/emit_web_hook_event.rb @@ -50,6 +50,18 @@ module Jobs args[:payload] = WebHookUserSerializer.new(user, scope: guardian, root: false).as_json end + def setup_group(args) + group = Group.find(args[:group_id]) + return if group.blank? + args[:payload] = WebHookGroupSerializer.new(group, scope: guardian, root: false).as_json + end + + def setup_category(args) + category = Category.find(args[:category_id]) + return if category.blank? + args[:payload] = WebHookCategorySerializer.new(category, scope: guardian, root: false).as_json + end + def ping_event?(event_type) event_type.to_s == 'ping'.freeze end diff --git a/app/models/category.rb b/app/models/category.rb index b2b7242873d..426da630adb 100644 --- a/app/models/category.rb +++ b/app/models/category.rb @@ -61,6 +61,9 @@ class Category < ActiveRecord::Base after_update :rename_category_definition, if: :saved_change_to_name? after_update :create_category_permalink, if: :saved_change_to_slug? + after_commit :trigger_category_created_event, on: :create + after_commit :trigger_category_destroyed_event, on: :destroy + belongs_to :parent_category, class_name: 'Category' has_many :subcategories, class_name: 'Category', foreign_key: 'parent_category_id' @@ -508,6 +511,16 @@ SQL def subcategory_list_includes_topics? subcategory_list_style.end_with?("with_featured_topics") end + + def trigger_category_created_event + DiscourseEvent.trigger(:category_created, self) + true + end + + def trigger_category_destroyed_event + DiscourseEvent.trigger(:category_destroyed, self) + true + end end # == Schema Information diff --git a/app/models/group.rb b/app/models/group.rb index 5d8e84436ec..d8fc3dd1916 100644 --- a/app/models/group.rb +++ b/app/models/group.rb @@ -35,6 +35,9 @@ class Group < ActiveRecord::Base after_save :expire_cache after_destroy :expire_cache + after_commit :trigger_group_created_event, on: :create + after_commit :trigger_group_destroyed_event, on: :destroy + def expire_cache ApplicationSerializer.expire_cache_fragment!("group_names") end @@ -580,6 +583,16 @@ class Group < ActiveRecord::Base self.member_of(groups, user).where("gu.owner") end + def trigger_group_created_event + DiscourseEvent.trigger(:group_created, self) + true + end + + def trigger_group_destroyed_event + DiscourseEvent.trigger(:group_destroyed, self) + true + end + protected def name_format_validator diff --git a/app/models/web_hook_event_type.rb b/app/models/web_hook_event_type.rb index d3596bfdaf5..a98bd5a99d0 100644 --- a/app/models/web_hook_event_type.rb +++ b/app/models/web_hook_event_type.rb @@ -2,6 +2,8 @@ class WebHookEventType < ActiveRecord::Base TOPIC = 1 POST = 2 USER = 3 + GROUP = 4 + CATEGORY = 5 has_and_belongs_to_many :web_hooks diff --git a/app/serializers/web_hook_category_serializer.rb b/app/serializers/web_hook_category_serializer.rb new file mode 100644 index 00000000000..02708ad1546 --- /dev/null +++ b/app/serializers/web_hook_category_serializer.rb @@ -0,0 +1,12 @@ +class WebHookCategorySerializer < CategorySerializer + + %i{ + can_edit + notification_level + }.each do |attr| + define_method("include_#{attr}?") do + false + end + end + +end diff --git a/app/serializers/web_hook_group_serializer.rb b/app/serializers/web_hook_group_serializer.rb new file mode 100644 index 00000000000..c580451db88 --- /dev/null +++ b/app/serializers/web_hook_group_serializer.rb @@ -0,0 +1,12 @@ +class WebHookGroupSerializer < GroupShowSerializer + + %i{ + is_group_user + is_group_owner + }.each do |attr| + define_method("include_#{attr}?") do + false + end + end + +end diff --git a/config/initializers/012-web_hook_events.rb b/config/initializers/012-web_hook_events.rb index 7e29c2d3011..158d09e3f0f 100644 --- a/config/initializers/012-web_hook_events.rb +++ b/config/initializers/012-web_hook_events.rb @@ -42,3 +42,23 @@ end WebHook.enqueue_hooks(:user, user_id: user.id, event_name: event.to_s) end end + +%i( + group_created + group_updated + group_destroyed +).each do |event| + DiscourseEvent.on(event) do |group| + WebHook.enqueue_hooks(:group, group_id: group.id, event_name: event.to_s) + end +end + +%i( + category_created + category_updated + category_destroyed +).each do |event| + DiscourseEvent.on(event) do |category| + WebHook.enqueue_hooks(:category, category_id: category.id, event_name: event.to_s) + end +end diff --git a/config/locales/client.en.yml b/config/locales/client.en.yml index efe2378a125..2ace41e8cd2 100644 --- a/config/locales/client.en.yml +++ b/config/locales/client.en.yml @@ -2927,6 +2927,12 @@ en: user_event: name: "User Event" details: "When a user logs in, logs out, is created, approved or updated." + group_event: + name: "Group Event" + details: "When a group is created, updated or destroyed." + category_event: + name: "Category Event" + details: "When a category is created, updated or destroyed." delivery_status: title: "Delivery Status" inactive: "Inactive" diff --git a/db/fixtures/007_web_hook_event_types.rb b/db/fixtures/007_web_hook_event_types.rb index d91b5f09a1a..c3f8b8b1509 100644 --- a/db/fixtures/007_web_hook_event_types.rb +++ b/db/fixtures/007_web_hook_event_types.rb @@ -12,3 +12,13 @@ WebHookEventType.seed do |b| b.id = WebHookEventType::USER b.name = "user" end + +WebHookEventType.seed do |b| + b.id = WebHookEventType::GROUP + b.name = "group" +end + +WebHookEventType.seed do |b| + b.id = WebHookEventType::CATEGORY + b.name = "category" +end diff --git a/spec/fabricators/web_hook_fabricator.rb b/spec/fabricators/web_hook_fabricator.rb index d9354fb59e6..937fc0fee67 100644 --- a/spec/fabricators/web_hook_fabricator.rb +++ b/spec/fabricators/web_hook_fabricator.rb @@ -21,6 +21,9 @@ Fabricator(:wildcard_web_hook, from: :web_hook) do wildcard_web_hook true end +Fabricator(:post_web_hook, from: :web_hook) do +end + Fabricator(:topic_web_hook, from: :web_hook) do transient topic_hook: WebHookEventType.find_by(name: 'topic') @@ -36,3 +39,19 @@ Fabricator(:user_web_hook, from: :web_hook) do web_hook.web_hook_event_types = [transients[:user_hook]] end end + +Fabricator(:group_web_hook, from: :web_hook) do + transient group_hook: WebHookEventType.find_by(name: 'group') + + after_build do |web_hook, transients| + web_hook.web_hook_event_types = [transients[:group_hook]] + end +end + +Fabricator(:category_web_hook, from: :web_hook) do + transient category_hook: WebHookEventType.find_by(name: 'category') + + after_build do |web_hook, transients| + web_hook.web_hook_event_types = [transients[:category_hook]] + end +end diff --git a/spec/jobs/emit_web_hook_event_spec.rb b/spec/jobs/emit_web_hook_event_spec.rb index b7e1cca39d5..29be1e69250 100644 --- a/spec/jobs/emit_web_hook_event_spec.rb +++ b/spec/jobs/emit_web_hook_event_spec.rb @@ -56,9 +56,14 @@ describe Jobs::EmitWebHookEvent do stub_request(:post, "https://meta.discourse.org/webhook_listener") .to_return(body: 'OK', status: 200) - expect do - subject.execute(web_hook_id: post_hook.id, event_type: 'post', post_id: post.id) - end.to change(WebHookEvent, :count).by(1) + WebHookEventType.all.pluck(:name).each do |name| + web_hook_id = Fabricate("#{name}_web_hook").id + object_id = Fabricate(name).id + + expect do + subject.execute(web_hook_id: web_hook_id, event_type: name, "#{name}_id": object_id) + end.to change(WebHookEvent, :count).by(1) + end end it 'skips silently on missing post' do diff --git a/spec/models/category_spec.rb b/spec/models/category_spec.rb index 9f756f394f1..51992b1ce56 100644 --- a/spec/models/category_spec.rb +++ b/spec/models/category_spec.rb @@ -368,6 +368,17 @@ describe Category do end end + describe 'new' do + subject { Fabricate.build(:category, user: Fabricate(:user)) } + + it 'triggers a extensibility event' do + event = DiscourseEvent.track_events { subject.save! }.last + + expect(event[:event_name]).to eq(:category_created) + expect(event[:params].first).to eq(subject) + end + end + describe "update" do it "should enforce uniqueness of slug" do Fabricate(:category, slug: "the-slug") @@ -384,14 +395,21 @@ describe Category do @category_id = @category.id @topic_id = @category.topic_id SiteSetting.shared_drafts_category = @category.id.to_s - @category.destroy end it 'is deleted correctly' do + @category.destroy expect(Category.exists?(id: @category_id)).to be false expect(Topic.exists?(id: @topic_id)).to be false expect(SiteSetting.shared_drafts_category).to be_blank end + + it 'triggers a extensibility event' do + event = DiscourseEvent.track_events { @category.destroy }.first + + expect(event[:event_name]).to eq(:category_destroyed) + expect(event[:params].first).to eq(@category) + end end describe 'latest' do diff --git a/spec/models/group_spec.rb b/spec/models/group_spec.rb index 20b1e242d14..255b1d245e2 100644 --- a/spec/models/group_spec.rb +++ b/spec/models/group_spec.rb @@ -411,17 +411,37 @@ describe Group do expect(g.usernames.split(",").sort).to eq usernames.split(",").sort end - it "correctly destroys groups" do + describe 'new' do + subject { Fabricate.build(:group) } - g = Fabricate(:group) - u1 = Fabricate(:user) - g.add(u1) - g.save! + it 'triggers a extensibility event' do + event = DiscourseEvent.track_events { subject.save! }.first - g.destroy + expect(event[:event_name]).to eq(:group_created) + expect(event[:params].first).to eq(subject) + end + end - expect(User.where(id: u1.id).count).to eq 1 - expect(GroupUser.where(group_id: g.id).count).to eq 0 + describe 'destroy' do + let(:user) { Fabricate(:user) } + let(:group) { Fabricate(:group, users: [user]) } + + before do + group.add(user) + end + + it "it deleted correctly" do + group.destroy! + expect(User.where(id: user.id).count).to eq 1 + expect(GroupUser.where(group_id: group.id).count).to eq 0 + end + + it 'triggers a extensibility event' do + event = DiscourseEvent.track_events { group.destroy! }.first + + expect(event[:event_name]).to eq(:group_destroyed) + expect(event[:params].first).to eq(group) + end end it "has custom fields" do diff --git a/spec/requests/categories_controller_spec.rb b/spec/requests/categories_controller_spec.rb new file mode 100644 index 00000000000..9934db2450a --- /dev/null +++ b/spec/requests/categories_controller_spec.rb @@ -0,0 +1,24 @@ +require 'rails_helper' + +describe GroupsController do + let(:admin) { Fabricate(:admin) } + let(:category) { Fabricate(:category, user: admin) } + + before do + category + sign_in(admin) + end + + it "triggers a extensibility event" do + event = DiscourseEvent.track_events { + put "/categories/#{category.id}.json", params: { + name: 'hello', + color: 'ff0', + text_color: 'fff' + } + }.last + + expect(event[:event_name]).to eq(:category_updated) + expect(event[:params].first).to eq(category) + end +end diff --git a/spec/requests/groups_controller_spec.rb b/spec/requests/groups_controller_spec.rb index 67e2afa1a29..5243092d817 100644 --- a/spec/requests/groups_controller_spec.rb +++ b/spec/requests/groups_controller_spec.rb @@ -450,6 +450,15 @@ describe GroupsController do expect(group.flair_color).to eq('BBB') expect(group.name).to eq('testing') end + + it 'triggers a extensibility event' do + event = DiscourseEvent.track_events { + put "/groups/#{group.id}.json", params: { group: { flair_color: 'BBB' } } + }.last + + expect(event[:event_name]).to eq(:group_updated) + expect(event[:params].first).to eq(group) + end end context "when user is not a group owner or admin" do