DEV: triggers DiscourseEvents when an event starts/ends

DEV: triggers DiscourseEvents when an event starts/ends

diff --git a/README.md b/README.md
index a5a08c4..fb13057 100644
--- a/README.md
+++ b/README.md
@@ -3,3 +3,10 @@
 Adds the ability to create a dynamic calendar in the first post of a topic.
 
 Topic discussing the plugin itself can be found here: [https://meta.discourse.org/t/discourse-calendar/97376](https://meta.discourse.org/t/discourse-calendar/97376)
+
+### Customisation
+
+#### Plugins
+
+- `discourse_post_event_event_started` this DiscourseEvent will be triggered when an event starts
+- `discourse_post_event_event_ended` this DiscourseEvent will be triggered when an event ends
diff --git a/app/models/discourse_post_event/event.rb b/app/models/discourse_post_event/event.rb
index 58fca7a..082124a 100644
--- a/app/models/discourse_post_event/event.rb
+++ b/app/models/discourse_post_event/event.rb
@@ -34,6 +34,28 @@ module DiscoursePostEvent
       end
     end
 
+    after_commit :setup_starts_at_handler, on: [:create, :update]
+    def setup_starts_at_handler
+      if !transaction_include_any_action?([:create])
+        Jobs.cancel_scheduled_job(:discourse_post_event_event_started, event_id: self.id)
+      end
+
+      if self.starts_at > Time.now
+        Jobs.enqueue_at(self.starts_at, :discourse_post_event_event_started, event_id: self.id)
+      end
+    end
+
+    after_commit :setup_ends_at_handler, on: [:create, :update]
+    def setup_ends_at_handler
+      if !transaction_include_any_action?([:create])
+        Jobs.cancel_scheduled_job(:discourse_post_event_event_ended, event_id: self.id)
+      end
+
+      if self.ends_at && self.ends_at > Time.now
+        Jobs.enqueue_at(self.ends_at, :discourse_post_event_event_ended, event_id: self.id)
+      end
+    end
+
     has_many :invitees, foreign_key: :post_id, dependent: :delete_all
     belongs_to :post, foreign_key: :id
 
diff --git a/jobs/regular/discourse_post_event/event_ended.rb b/jobs/regular/discourse_post_event/event_ended.rb
new file mode 100644
index 0000000..3ba66e4
--- /dev/null
+++ b/jobs/regular/discourse_post_event/event_ended.rb
@@ -0,0 +1,13 @@
+# frozen_string_literal: true
+
+module Jobs
+  class DiscoursePostEventEventEnded < ::Jobs::Base
+    sidekiq_options retry: false
+
+    def execute(args)
+      raise Discourse::InvalidParameters.new(:event_id) if args[:event_id].blank?
+      event = Event.find(args[:event_id])
+      DiscourseEvent.trigger(:discourse_post_event_event_ended, event)
+    end
+  end
+end
diff --git a/jobs/regular/discourse_post_event/event_started.rb b/jobs/regular/discourse_post_event/event_started.rb
new file mode 100644
index 0000000..778c066
--- /dev/null
+++ b/jobs/regular/discourse_post_event/event_started.rb
@@ -0,0 +1,13 @@
+# frozen_string_literal: true
+
+module Jobs
+  class DiscoursePostEventEventStarted < ::Jobs::Base
+    sidekiq_options retry: false
+
+    def execute(args)
+      raise Discourse::InvalidParameters.new(:event_id) if args[:event_id].blank?
+      event = Event.find(args[:event_id])
+      DiscourseEvent.trigger(:discourse_post_event_event_started, event)
+    end
+  end
+end
diff --git a/plugin.rb b/plugin.rb
index 4fb4c43..83ce74a 100644
--- a/plugin.rb
+++ b/plugin.rb
@@ -87,6 +87,8 @@ after_initialize do
     "../lib/discourse_post_event/event_parser.rb",
     "../lib/discourse_post_event/event_validator.rb",
     "../jobs/regular/discourse_post_event/bulk_invite.rb",
+    "../jobs/regular/discourse_post_event/event_started.rb",
+    "../jobs/regular/discourse_post_event/event_ended.rb",
     "../lib/discourse_post_event/event_finder.rb",
     "../app/serializers/discourse_post_event/invitee_serializer.rb",
     "../app/serializers/discourse_post_event/event_serializer.rb"
diff --git a/spec/models/discourse_post_event/event_spec.rb b/spec/models/discourse_post_event/event_spec.rb
index dc6beea..bc5fff1 100644
--- a/spec/models/discourse_post_event/event_spec.rb
+++ b/spec/models/discourse_post_event/event_spec.rb
@@ -47,6 +47,100 @@ describe DiscoursePostEvent::Event do
             expect(second_post.topic.custom_fields).to be_blank
           end
         end
+
+        context 'setting dates enqueues future jobs at date' do
+          before do
+            Jobs.run_later!
+          end
+
+          context 'starts_at' do
+            context 'is after current time' do
+              it 'queues a future discourse event trigger' do
+                expect_enqueued_with(job: :discourse_post_event_event_started, args: {
+                  "event_id" => first_post.id
+                }) do
+                  Event.create!(id: first_post.id, starts_at: starts_at)
+                end
+              end
+            end
+
+            context 'is before current time' do
+              it 'doesn’t queues a future discourse event trigger' do
+                expect {
+                  Event.create!(id: first_post.id, starts_at: Time.now - 1.day)
+                }.to change {
+                  Jobs::DiscoursePostEventEventStarted.jobs.count
+                }.by(0)
+              end
+            end
+
+            context 'an event started job was already scheduled' do
+              it 'queues a future discourse event trigger' do
+                Jobs
+                  .expects(:cancel_scheduled_job)
+                  .with(:discourse_post_event_event_ended, event_id: first_post.id)
+                  .once
+
+                Jobs
+                  .expects(:cancel_scheduled_job)
+                  .with(:discourse_post_event_event_started, event_id: first_post.id)
+                  .once
+
+                Event.create!(id: first_post.id, starts_at: starts_at)
+
+                expect(Jobs::DiscoursePostEventEventStarted.jobs.count).to eq(1)
+
+                Event.find(first_post.id).update!(starts_at: Time.now + 2.hours)
+
+                expect(Jobs::DiscoursePostEventEventStarted.jobs.count).to eq(2)
+              end
+            end
+          end
+
+          context 'ends_at' do
+            context 'is after current time' do
+              it 'queues a future discourse event trigger' do
+                expect_enqueued_with(job: :discourse_post_event_event_ended, args: {
+                  "event_id" => first_post.id
+                }) do
+                  Event.create!(id: first_post.id, starts_at: Time.now - 1.day, ends_at: Time.now + 12.hours)
+                end
+              end
+            end
+
+            context 'is before current time' do
+              it 'doesn’t queue a future discourse event trigger' do
+                expect {
+                  Event.create!(id: first_post.id, starts_at: Time.now - 1.day, ends_at: Time.now - 12.hours)
+                }.to change {
+                  Jobs::DiscoursePostEventEventEnded.jobs.count
+                }.by(0)
+              end
+            end
+
+            context 'an event ended job was already scheduled' do
+              it 'queues a future discourse event trigger' do
+                Jobs
+                  .expects(:cancel_scheduled_job)
+                  .with(:discourse_post_event_event_ended, event_id: first_post.id)
+                  .once
+
+                Jobs
+                  .expects(:cancel_scheduled_job)
+                  .with(:discourse_post_event_event_started, event_id: first_post.id)
+                  .once
+
+                Event.create!(id: first_post.id, starts_at: Time.now - 1.day, ends_at: Time.now + 12.hours)
+
+                expect(Jobs::DiscoursePostEventEventEnded.jobs.count).to eq(1)
+
+                Event.find(first_post.id).update!(starts_at: Time.now - 1.day, ends_at: Time.now + 13.hours)
+
+                expect(Jobs::DiscoursePostEventEventEnded.jobs.count).to eq(2)
+              end
+            end
+          end
+        end
       end
 
       context 'a post event has been updated' do

GitHub sha: bdf4942e