FEATURE: Add 'New users only' option to user_updated trigger (#26648)

This commit adds a new option to the `user_updated` trigger of the automation plugin to only trigger an automation for new users that join after the automation is enabled.

Internal topic: t/125829/9.
This commit is contained in:
Osama Sayegh 2024-04-16 21:13:11 +03:00 committed by GitHub
parent 57d29b6f3b
commit 4733369f71
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 190 additions and 2 deletions

View File

@ -20,6 +20,10 @@ module DiscourseAutomation
validates :script, presence: true validates :script, presence: true
validate :validate_trigger_fields validate :validate_trigger_fields
after_destroy do |automation|
UserCustomField.where(name: automation.new_user_custom_field_name).destroy_all
end
attr_accessor :running_in_background attr_accessor :running_in_background
def running_in_background! def running_in_background!
@ -163,6 +167,10 @@ module DiscourseAutomation
scriptable&.on_reset&.call(self) scriptable&.on_reset&.call(self)
end end
def new_user_custom_field_name
"automation_#{self.id}_new_user"
end
private private
def validate_trigger_fields def validate_trigger_fields

View File

@ -175,6 +175,9 @@ en:
once_per_user: once_per_user:
label: Once per user label: Once per user
description: Will trigger only once per user description: Will trigger only once per user
new_users_only:
label: New users only
description: Will trigger only for new users that join after this automation is enabled
category_created_edited: category_created_edited:
fields: fields:
restricted_category: restricted_category:

View File

@ -65,7 +65,7 @@ module DiscourseAutomation
end end
end end
def self.handle_user_updated(user) def self.handle_user_updated(user, new_user: false)
return if user.id < 0 return if user.id < 0
name = DiscourseAutomation::Triggers::USER_UPDATED name = DiscourseAutomation::Triggers::USER_UPDATED
@ -79,6 +79,13 @@ module DiscourseAutomation
next next
end end
new_users_only = automation.trigger_field("new_users_only")["value"]
new_user_custom_field = automation.new_user_custom_field_name
new_user ||= user.custom_fields[new_user_custom_field].present?
next if new_users_only && !new_user
required_custom_fields = automation.trigger_field("custom_fields") required_custom_fields = automation.trigger_field("custom_fields")
user_data = {} user_data = {}
user_custom_fields_data = DB.query <<-SQL user_custom_fields_data = DB.query <<-SQL
@ -87,16 +94,22 @@ module DiscourseAutomation
JOIN user_custom_fields ucf ON CONCAT('user_field_', uf.id) = ucf.name JOIN user_custom_fields ucf ON CONCAT('user_field_', uf.id) = ucf.name
WHERE ucf.user_id = #{user.id}; WHERE ucf.user_id = #{user.id};
SQL SQL
user_custom_fields_data = user_custom_fields_data =
user_custom_fields_data.each_with_object({}) do |obj, hash| user_custom_fields_data.each_with_object({}) do |obj, hash|
field_name = obj.field_name field_name = obj.field_name
field_value = obj.field_value field_value = obj.field_value
hash[field_name] = field_value hash[field_name] = field_value
end end
if required_custom_fields["value"] if required_custom_fields["value"]
if required_custom_fields["value"].any? { |field| if required_custom_fields["value"].any? { |field|
user_custom_fields_data[field].blank? user_custom_fields_data[field].blank?
} }
if new_users_only
user.custom_fields[new_user_custom_field] = "1"
user.save_custom_fields
end
next next
end end
user_data[:custom_fields] = user_custom_fields_data user_data[:custom_fields] = user_custom_fields_data
@ -108,11 +121,20 @@ module DiscourseAutomation
if required_user_profile_fields["value"].any? { |field| if required_user_profile_fields["value"].any? { |field|
user_profile_data[field].blank? user_profile_data[field].blank?
} }
if new_users_only
user.custom_fields[new_user_custom_field] = "1"
user.save_custom_fields
end
next next
end end
user_data[:profile_data] = user_profile_data user_data[:profile_data] = user_profile_data
end end
if new_users_only && once_per_user
user.custom_fields.delete(new_user_custom_field)
user.save_custom_fields
end
automation.attach_custom_field(user) automation.attach_custom_field(user)
automation.trigger!("kind" => name, "user" => user, "user_data" => user_data) automation.trigger!("kind" => name, "user" => user, "user_data" => user_data)
end end

View File

@ -8,6 +8,7 @@ DiscourseAutomation::Triggerable.add(DiscourseAutomation::Triggers::USER_UPDATED
field :custom_fields, component: :custom_fields field :custom_fields, component: :custom_fields
field :user_profile, component: :user_profile field :user_profile, component: :user_profile
field :once_per_user, component: :boolean field :once_per_user, component: :boolean
field :new_users_only, component: :boolean
validate do validate do
has_triggers = has_trigger_field?(:custom_fields) && has_trigger_field?(:user_profile) has_triggers = has_trigger_field?(:custom_fields) && has_trigger_field?(:user_profile)

View File

@ -208,6 +208,9 @@ after_initialize do
register_topic_custom_field_type(DiscourseAutomation::AUTO_RESPONDER_TRIGGERED_IDS, [:integer]) register_topic_custom_field_type(DiscourseAutomation::AUTO_RESPONDER_TRIGGERED_IDS, [:integer])
on(:user_updated) { |user| DiscourseAutomation::EventHandlers.handle_user_updated(user) } on(:user_updated) { |user| DiscourseAutomation::EventHandlers.handle_user_updated(user) }
on(:user_created) do |user|
DiscourseAutomation::EventHandlers.handle_user_updated(user, new_user: true)
end
register_user_custom_field_type(DiscourseAutomation::CUSTOM_FIELD, [:integer]) register_user_custom_field_type(DiscourseAutomation::CUSTOM_FIELD, [:integer])
register_post_custom_field_type(DiscourseAutomation::CUSTOM_FIELD, [:integer]) register_post_custom_field_type(DiscourseAutomation::CUSTOM_FIELD, [:integer])

View File

@ -165,4 +165,21 @@ describe DiscourseAutomation::Automation do
end end
end end
end end
describe "after_destroy" do
fab!(:automation) { Fabricate(:automation, enabled: false) }
fab!(:automation2) { Fabricate(:automation, enabled: false) }
it "deletes user custom fields that indicate new users" do
user = Fabricate(:user)
user.custom_fields[automation.new_user_custom_field_name] = "1"
user.custom_fields[automation2.new_user_custom_field_name] = "1"
user.save_custom_fields
automation.destroy!
user.reload
expect(user.custom_fields).to eq({ automation2.new_user_custom_field_name => "1" })
end
end
end end

View File

@ -93,7 +93,7 @@ describe "UserUpdated" do
end end
end end
context "when once_per_user is no set" do context "when once_per_user is not set" do
it "triggers every time" do it "triggers every time" do
output = output =
capture_contexts { UserUpdater.new(user, user).update(location: "Japan", bio_raw: "fine") } capture_contexts { UserUpdater.new(user, user).update(location: "Japan", bio_raw: "fine") }
@ -124,4 +124,138 @@ describe "UserUpdated" do
expect(output.first["kind"]).to eq("user_updated") expect(output.first["kind"]).to eq("user_updated")
end end
end end
context "when new_users_only is set" do
before do
automation.upsert_field!("new_users_only", "boolean", { value: true }, target: "trigger")
end
it "triggers for new users" do
user = nil
output =
capture_contexts do
user = Fabricate(:user)
user.set_user_field(user_field_1.id, "Answer new custom 1")
user.set_user_field(user_field_2.id, "Answer new custom 2")
UserUpdater.new(user, user).update(location: "Japan", bio_raw: "fine")
end
expect(output.size).to eq(1)
expect(output.first["kind"]).to eq("user_updated")
expect(output.first["user"].id).to eq(user.id)
expect(output.first["user_data"][:custom_fields]).to eq(
{ "custom field 1" => "Answer new custom 1", "custom field 2" => "Answer new custom 2" },
)
expect(output.first["user_data"][:profile_data]["location"]).to eq("Japan")
expect(output.first["user_data"][:profile_data]["bio_raw"]).to eq("fine")
output =
capture_contexts do
UserUpdater.new(user, user).update(location: "Japan22", bio_raw: "finegood")
end
expect(output.size).to eq(1)
expect(output.first["kind"]).to eq("user_updated")
expect(output.first["user"].id).to eq(user.id)
expect(output.first["user_data"][:profile_data]["location"]).to eq("Japan22")
expect(output.first["user_data"][:profile_data]["bio_raw"]).to eq("finegood")
end
it "doesn't trigger for existing users" do
output =
capture_contexts { UserUpdater.new(user, user).update(location: "Japan", bio_raw: "fine") }
expect(output).to eq([])
end
context "when once_per_user is set" do
before do
automation.upsert_field!("once_per_user", "boolean", { value: true }, target: "trigger")
end
it "triggers only once for a new user" do
user = nil
output =
capture_contexts do
user = Fabricate(:user)
user.set_user_field(user_field_1.id, "Answer new custom 1")
user.set_user_field(user_field_2.id, "Answer new custom 2")
UserUpdater.new(user, user).update(location: "Japan", bio_raw: "fine")
end
expect(output.size).to eq(1)
expect(output.first["kind"]).to eq("user_updated")
expect(output.first["user"].id).to eq(user.id)
expect(output.first["user_data"][:custom_fields]).to eq(
{ "custom field 1" => "Answer new custom 1", "custom field 2" => "Answer new custom 2" },
)
expect(output.first["user_data"][:profile_data]["location"]).to eq("Japan")
expect(output.first["user_data"][:profile_data]["bio_raw"]).to eq("fine")
output =
capture_contexts do
UserUpdater.new(user, user).update(location: "Japan22", bio_raw: "finegood")
end
expect(output).to eq([])
end
it "doesn't trigger for an existing user" do
output =
capture_contexts do
UserUpdater.new(user, user).update(location: "Japan", bio_raw: "fine")
end
expect(output).to eq([])
end
end
end
context "when new_users_only is not set" do
before do
automation.upsert_field!("new_users_only", "boolean", { value: false }, target: "trigger")
end
it "triggers for new users" do
user = nil
output =
capture_contexts do
user = Fabricate(:user)
user.set_user_field(user_field_1.id, "Answer new custom 1")
user.set_user_field(user_field_2.id, "Answer new custom 2")
UserUpdater.new(user, user).update(location: "Japan", bio_raw: "fine")
end
expect(output.size).to eq(1)
expect(output.first["kind"]).to eq("user_updated")
expect(output.first["user"].id).to eq(user.id)
expect(output.first["user_data"][:custom_fields]).to eq(
{ "custom field 1" => "Answer new custom 1", "custom field 2" => "Answer new custom 2" },
)
expect(output.first["user_data"][:profile_data]["location"]).to eq("Japan")
expect(output.first["user_data"][:profile_data]["bio_raw"]).to eq("fine")
end
it "triggers for existing users" do
output =
capture_contexts { UserUpdater.new(user, user).update(location: "Japan", bio_raw: "fine") }
expect(output.size).to eq(1)
expect(output.first["kind"]).to eq("user_updated")
expect(output.first["user"].id).to eq(user.id)
expect(output.first["user_data"][:custom_fields]).to eq(
{ "custom field 1" => "Answer custom 1", "custom field 2" => "Answer custom 2" },
)
expect(output.first["user_data"][:profile_data]["location"]).to eq("Japan")
expect(output.first["user_data"][:profile_data]["bio_raw"]).to eq("fine")
output =
capture_contexts do
UserUpdater.new(user, user).update(location: "Japan22", bio_raw: "finegood")
end
expect(output.size).to eq(1)
expect(output.first["kind"]).to eq("user_updated")
expect(output.first["user"].id).to eq(user.id)
expect(output.first["user_data"][:profile_data]["location"]).to eq("Japan22")
expect(output.first["user_data"][:profile_data]["bio_raw"]).to eq("finegood")
end
end
end end