Skip to content

Commit

Permalink
feat: add cron? trigger flag
Browse files Browse the repository at this point in the history
If `true` or not provided, the cron configuration for the trigger
will proceed as normal.

If `false`, no cron configuration will be added for the trigger.
The only way to run the trigger is manually, via `run_trigger`.
  • Loading branch information
rellen committed Aug 10, 2023
1 parent 1286db7 commit eb01d62
Show file tree
Hide file tree
Showing 7 changed files with 262 additions and 8 deletions.
1 change: 1 addition & 0 deletions .formatter.exs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
spark_locals_without_parens = [
action: 1,
api: 1,
cron?: 1,
max_attempts: 1,
max_scheduler_attempts: 1,
on_error: 1,
Expand Down
17 changes: 15 additions & 2 deletions lib/ash_oban.ex
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,8 @@ defmodule AshOban do
state: :active | :paused | :deleted,
worker: module,
__identifier__: atom,
on_error: atom
on_error: atom,
cron?: boolean
}

defstruct [
Expand All @@ -38,6 +39,7 @@ defmodule AshOban do
:scheduler,
:worker,
:on_error,
:cron?,
:__identifier__
]

Expand Down Expand Up @@ -140,6 +142,12 @@ defmodule AshOban do
type: :atom,
doc:
"An update action to call after the last attempt has failed. See the getting started guide for more."
],
cron?: [
type: :boolean,
default: true,
doc:
"Whether to configure the scheduler and the Oban cron plugin to run the trigger on a cron schedule. Defaults to true."
]
]
}
Expand Down Expand Up @@ -252,7 +260,12 @@ defmodule AshOban do
end)
|> Enum.reduce(base, fn {resource, trigger}, config ->
require_queues!(config, resource, trigger)
add_job(config, cron_plugin, resource, trigger)

if trigger.cron? do
add_job(config, cron_plugin, resource, trigger)
else
config
end
end)
end

Expand Down
3 changes: 1 addition & 2 deletions lib/test.ex
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,7 @@ defmodule AshOban.Test do
@moduledoc "Helpers for testing ash_oban triggers"

def schedule_and_run_triggers(resource) do
triggers =
AshOban.Info.oban_triggers(resource)
triggers = AshOban.Info.oban_triggers(resource)

Enum.each(triggers, fn trigger ->
AshOban.schedule(resource, trigger)
Expand Down
10 changes: 8 additions & 2 deletions lib/transformers/define_schedulers.ex
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,14 @@ defmodule AshOban.Transformers.DefineSchedulers do
|> Transformer.async_compile(fn ->
define_worker(module, worker_module_name, trigger, dsl)
end)
|> Transformer.async_compile(fn ->
define_scheduler(module, scheduler_module_name, worker_module_name, trigger, dsl)
|> then(fn dsl ->
if trigger.cron? do
Transformer.async_compile(dsl, fn ->
define_scheduler(module, scheduler_module_name, worker_module_name, trigger, dsl)
end)
else
dsl
end
end)
end)
|> then(&{:ok, &1})
Expand Down
3 changes: 2 additions & 1 deletion lib/transformers/set_defaults.ex
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,8 @@ defmodule AshOban.Transformers.SetDefaults do
| read_action: read_action.name,
queue: queue,
scheduler_queue: trigger.scheduler_queue || queue,
action: trigger.action || trigger.name
action: trigger.action || trigger.name,
cron?: if(is_nil(trigger.cron?), do: true, else: trigger.cron?)
})
end)}
end
Expand Down
3 changes: 2 additions & 1 deletion test/ash_oban_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ defmodule AshObanTest do
doctest AshOban

defmodule Api do
use Ash.Api
use Ash.Api, validate_config_inclusion?: false

resources do
allow_unregistered? true
Expand All @@ -12,6 +12,7 @@ defmodule AshObanTest do

defmodule Triggered do
use Ash.Resource,
validate_api_inclusion?: false,
data_layer: Ash.DataLayer.Ets,
extensions: [AshOban]

Expand Down
233 changes: 233 additions & 0 deletions test/cron_flag_test.exs
Original file line number Diff line number Diff line change
@@ -0,0 +1,233 @@
defmodule CronFlagTest do
use ExUnit.Case
doctest AshOban

defmodule Api do
use Ash.Api, validate_config_inclusion?: false

resources do
registry CronFlagTest.Registry
end
end

defmodule Registry do
use Ash.Registry

entries do
entry CronFlagTest.DefaultTrigger
entry CronFlagTest.CronTrigger
entry CronFlagTest.NonCronTrigger
end
end

defmodule DefaultTrigger do
use Ash.Resource,
validate_api_inclusion?: false,
data_layer: Ash.DataLayer.Ets,
extensions: [AshOban]

oban do
triggers do
api Api

trigger :process do
action :process
where expr(processed != true)
worker_read_action(:read)
end
end
end

actions do
defaults [:create]

read :read do
primary? true
pagination keyset?: true
end

update :process do
change set_attribute(:processed, true)
end
end

ets do
private? true
end

attributes do
uuid_primary_key :id
end
end

defmodule CronTrigger do
use Ash.Resource,
validate_api_inclusion?: false,
data_layer: Ash.DataLayer.Ets,
extensions: [AshOban]

oban do
triggers do
api Api

trigger :process do
action :process
where expr(processed != true)
worker_read_action(:read)
cron? true
end
end
end

actions do
defaults [:create]

read :read do
primary? true
pagination keyset?: true
end

update :process do
change set_attribute(:processed, true)
end
end

ets do
private? true
end

attributes do
uuid_primary_key :id
end
end

defmodule NonCronTrigger do
use Ash.Resource,
validate_api_inclusion?: false,
data_layer: Ash.DataLayer.Ets,
extensions: [AshOban]

oban do
triggers do
api Api

trigger :process do
action :process
where expr(processed != true)
worker_read_action(:read)
cron? false
end
end
end

actions do
defaults [:create]

read :read do
primary? true
pagination keyset?: true
end

update :process do
change set_attribute(:processed, true)
end
end

ets do
private? true
end

attributes do
uuid_primary_key :id
end
end

describe "Cron? flag" do
test "defaults to true if not provided" do
assert [%AshOban.Trigger{cron?: true}] = AshOban.Info.oban_triggers(DefaultTrigger)
end

test "is correctly set from DSL when provided" do
assert [%AshOban.Trigger{cron?: true}] = AshOban.Info.oban_triggers(CronTrigger)
assert [%AshOban.Trigger{cron?: false}] = AshOban.Info.oban_triggers(NonCronTrigger)
end

test "does not create scheduler module if set to false" do
refute Code.ensure_loaded?(NonCronTrigger.AshOban.Scheduler.Process)
end

test "creates scheduler module if not provided" do
assert Code.ensure_loaded?(DefaultTrigger.AshOban.Scheduler.Process)
end

test "creates scheduler module if set to true" do
assert Code.ensure_loaded?(CronTrigger.AshOban.Scheduler.Process)
end

test "does not configure a cron worker for trigger with cron? flag set to false" do
config =
AshOban.config([CronFlagTest.Api],
plugins: [
{Oban.Plugins.Cron, crontab: []}
],
queues: [
default_trigger_process: 1,
cron_trigger_process: 1,
non_cron_trigger_process: 1
]
)

plugins = config[:plugins]
cron = plugins[Oban.Plugins.Cron]
cron_tab = cron[:crontab]

refute Enum.any?(cron_tab, fn {_spec, module, _opts} ->
module == NonCronTrigger.AshOban.Scheduler.Process
end)
end

test "configures a cron worker for trigger with cron? flag set to true" do
config =
AshOban.config([CronFlagTest.Api],
plugins: [
{Oban.Plugins.Cron, crontab: []}
],
queues: [
default_trigger_process: 1,
cron_trigger_process: 1,
non_cron_trigger_process: 1
]
)

plugins = config[:plugins]
cron = plugins[Oban.Plugins.Cron]
cron_tab = cron[:crontab]

assert Enum.any?(cron_tab, fn {_spec, module, _opts} ->
module == CronTrigger.AshOban.Scheduler.Process
end)
end

test "configures a cron worker for trigger with cron? flag not provided" do
config =
AshOban.config([CronFlagTest.Api],
plugins: [
{Oban.Plugins.Cron, crontab: []}
],
queues: [
default_trigger_process: 1,
cron_trigger_process: 1,
non_cron_trigger_process: 1
]
)

plugins = config[:plugins]
cron = plugins[Oban.Plugins.Cron]
cron_tab = cron[:crontab]

assert Enum.any?(cron_tab, fn {_spec, module, _opts} ->
module == DefaultTrigger.AshOban.Scheduler.Process
end)
end
end
end

0 comments on commit eb01d62

Please sign in to comment.