Repository: quantum-elixir/quantum-core
Pull request #459 (2020-09-24 15:30:14)

Solution: Support manual job triggering

coveralls: ## Pull Request Test Coverage Report for [Build 37ca53f30637c39ba2ea97433a119b511f5ec599-PR-459](https://coveralls.io/builds/33697566)

* **10** of **11**   **(90.91%)**  changed or added relevant lines in **2** files are covered.
* No unchanged relevant lines lost coverage.
* Overall coverage increased (+**0.1%**) to **87.306%**

---

|  Changes Missing Coverage | Covered Lines | Changed/Added Lines | % |
| :-----|--------------|--------|---: |
| [lib/quantum/job_broadcaster.ex](https://coveralls.io/builds/33697566/source?filename=lib%2Fquantum%2Fjob_broadcaster.ex#L317) | 7 | 8 | 87.5%
<!-- | **Total:** | **10** | **11** | **90.91%** | -->


|  Totals | [![Coverage Status](https://coveralls.io/builds/33697566/badge)](https://coveralls.io/builds/33697566) |
| :-- | --: |
| Change from base [Build 8f002322d85f6657d76154b3d34a84446f65ed3b](https://coveralls.io/builds/33438446): |  0.1% |
| Covered Lines: | 337 |
| Relevant Lines: | 386 |

---
##### 💛  - [Coveralls](https://coveralls.io)


Patch:
diff --git a/lib/quantum.ex b/lib/quantum.ex
index a6dd626..8478dfe 100644
--- a/lib/quantum.ex
+++ b/lib/quantum.ex
@@ -148,6 +148,11 @@ defmodule Quantum do
   """
   @callback activate_job(GenStage.stage(), atom) :: :ok
 
+  @doc """
+  Runs a job by name once
+  """
+  @callback run_job(GenStage.stage(), atom) :: :ok
+
   @doc """
   Resolves a job by name
   """
@@ -289,6 +294,12 @@ defmodule Quantum do
         GenStage.cast(server, {:change_state, name, :active})
       end
 
+      @impl behaviour
+      def run_job(server \\ __job_broadcaster__(), name)
+          when is_atom(name) or is_reference(name) do
+        GenStage.cast(server, {:run_job, name})
+      end
+
       @impl behaviour
       def find_job(server \\ __job_broadcaster__(), name)
           when is_atom(name) or is_reference(name) do
diff --git a/lib/quantum/execution_broadcaster.ex b/lib/quantum/execution_broadcaster.ex
index 03e76af..5a0ea10 100644
--- a/lib/quantum/execution_broadcaster.ex
+++ b/lib/quantum/execution_broadcaster.ex
@@ -121,6 +121,18 @@ defmodule Quantum.ExecutionBroadcaster do
     {[], %{state | uninitialized_jobs: [job | uninitialized_jobs]}}
   end
 
+  def handle_event(
+        {:run, %Job{name: name} = job},
+        %State{debug_logging: debug_logging} = state
+      ) do
+    debug_logging &&
+      Logger.debug(fn ->
+        "[#{inspect(Node.self())}][#{__MODULE__}] Running job #{inspect(name)} once"
+      end)
+
+    {[%ExecuteEvent{job: job}], state}
+  end
+
   def handle_event(
         {:remove, name},
         %State{
diff --git a/lib/quantum/job_broadcaster.ex b/lib/quantum/job_broadcaster.ex
index 9804e3d..03a8812 100644
--- a/lib/quantum/job_broadcaster.ex
+++ b/lib/quantum/job_broadcaster.ex
@@ -300,6 +300,27 @@ defmodule Quantum.JobBroadcaster do
     end
   end
 
+  def handle_cast(
+        {:run_job, name},
+        %State{
+          jobs: jobs,
+          debug_logging: debug_logging
+        } = state
+      ) do
+    debug_logging &&
+      Logger.debug(fn ->
+        "[#{inspect(Node.self())}][#{__MODULE__}] Running job #{inspect(name)} once"
+      end)
+
+    case Map.fetch(jobs, name) do
+      :error ->
+        {:noreply, [], state}
+
+      {:ok, job} ->
+        {:noreply, [{:run, job}], state}
+    end
+  end
+
   def handle_cast(
         :delete_all,
         %State{
diff --git a/pages/runtime.md b/pages/runtime.md
index d161e06..7d8568a 100644
--- a/pages/runtime.md
+++ b/pages/runtime.md
@@ -30,6 +30,11 @@ Activate an inactive job:
 YourApp.Scheduler.activate_job(:ticker)
 ```
 
+Run a job once outside of normal schedule:
+```elixir
+YourApp.Scheduler.run_job(:ticker)
+```
+
 Find a job:
 ```elixir
 YourApp.Scheduler.find_job(:ticker)


Test patch:
diff --git a/test/quantum/execution_broadcaster_test.exs b/test/quantum/execution_broadcaster_test.exs
index 523e7d0..9f3ae28 100644
--- a/test/quantum/execution_broadcaster_test.exs
+++ b/test/quantum/execution_broadcaster_test.exs
@@ -77,6 +77,14 @@ defmodule Quantum.ExecutionBroadcasterTest do
       end)
     end
 
+    test "run_job triggers job to run once", %{producer: producer} do
+      job = TestScheduler.new_job()
+
+      TestProducer.send(producer, {:run, job})
+
+      assert_receive {:received, %ExecuteEvent{job: ^job}}
+    end
+
     test "normal schedule triggers once per second", %{producer: producer} do
       job =
         TestScheduler.new_job()
diff --git a/test/quantum/job_broadcaster_test.exs b/test/quantum/job_broadcaster_test.exs
index 1c27347..f8f408c 100644
--- a/test/quantum/job_broadcaster_test.exs
+++ b/test/quantum/job_broadcaster_test.exs
@@ -213,6 +213,19 @@ defmodule Quantum.JobBroadcasterTest do
              end) =~ "Adding job #Reference"
     end
 
+    @tag listen_storage: true
+    test "run", %{broadcaster: broadcaster, active_job: active_job} do
+      test_id = "run-job-handler"
+
+      :ok = attach_telemetry(:run, test_id, self())
+
+      TestScheduler.add_job(broadcaster, active_job)
+      TestScheduler.run_job(broadcaster, active_job.name)
+
+      assert_receive {:received, {:add, ^active_job}}
+      assert_receive {:received, {:run, ^active_job}}
+    end
+
     @tag listen_storage: true
     test "inactive", %{broadcaster: broadcaster, inactive_job: inactive_job} do
       test_id = "add-inactive-job-handler"