Previous commit (3c988a5f) switched oban_pro to the licensed hex repo at deploy time. Reverting that — keep all three Pro packages vendored so prod image builds don't depend on oban.pro reachability or auth on every CI run. oban_pro 1.7.0 dropped into vendor/oban_pro via `mix hex.package fetch oban_pro 1.7.0 --repo=oban --unpack`. mix.exs goes back to `path: "vendor/oban_pro"` (matching oban_met / oban_web, which were already vendored — and stay vendored since the licensed repo only has older versions of those: 0.1.11 / 2.10.6 vs the 1.1.0 / 2.12.1 we vendor). Schema migration from3c988a5fstays — 1.7.0 tables/indexes are already applied. No code changes.
125 lines
3.2 KiB
Elixir
125 lines
3.2 KiB
Elixir
defmodule Oban.Pro.Cron do
|
|
@moduledoc false
|
|
|
|
use Ecto.Schema
|
|
|
|
import Ecto.Changeset
|
|
|
|
alias Oban.Cron.Expression
|
|
alias Oban.Worker
|
|
|
|
@primary_key {:name, :string, autogenerate: false}
|
|
schema "oban_crons" do
|
|
field :expression, :string
|
|
field :worker, :string
|
|
field :opts, :map
|
|
field :paused, :boolean
|
|
field :insertions, {:array, :utc_datetime_usec}
|
|
field :lock_version, :integer, default: 1
|
|
field :parsed, :any, virtual: true
|
|
|
|
timestamps(
|
|
inserted_at: :inserted_at,
|
|
updated_at: :updated_at,
|
|
type: :utc_datetime_usec
|
|
)
|
|
end
|
|
|
|
@permitted ~w(name expression insertions worker opts paused)a
|
|
@requried ~w(name expression worker)a
|
|
@allowed_opts ~w(args guaranteed max_attempts meta priority queue tags timezone)a
|
|
|
|
@doc false
|
|
def allowed_opts, do: @allowed_opts
|
|
|
|
@spec changeset({binary(), module()} | {binary(), module(), Keyword.t()}) :: Ecto.Changeset.t()
|
|
def changeset({expression, worker}) do
|
|
params = %{expression: expression, name: worker, worker: worker, opts: %{}}
|
|
|
|
changeset(%__MODULE__{}, params)
|
|
end
|
|
|
|
def changeset({expression, worker, opts}) do
|
|
{name, opts} = Keyword.pop(opts, :name, worker)
|
|
{paused, opts} = Keyword.pop(opts, :paused)
|
|
{insertions, opts} = Keyword.pop(opts, :insertions)
|
|
|
|
params = %{
|
|
expression: expression,
|
|
insertions: insertions,
|
|
name: name,
|
|
opts: Map.new(opts),
|
|
paused: paused,
|
|
worker: worker
|
|
}
|
|
|
|
changeset(%__MODULE__{}, params)
|
|
end
|
|
|
|
@spec changeset({Ecto.Schema.t(), map()}) :: Ecto.Changeset.t()
|
|
def changeset(schema, params) when is_map(params) do
|
|
params =
|
|
params
|
|
|> coerce_name(:name)
|
|
|> coerce_name(:worker)
|
|
|> merge_opts(schema)
|
|
|
|
schema
|
|
|> cast(params, @permitted)
|
|
|> validate_required(@requried)
|
|
|> validate_change(:expression, &expression_validator/2)
|
|
|> validate_change(:opts, &opts_validator/2)
|
|
|> optimistic_lock(:lock_version)
|
|
end
|
|
|
|
def update_changeset(schema, params) when is_map(params) do
|
|
if Map.has_key?(params, :expression) or Map.has_key?(params, :timezone) do
|
|
changeset(schema, Map.put(params, :insertions, []))
|
|
else
|
|
changeset(schema, params)
|
|
end
|
|
end
|
|
|
|
defp coerce_name(params, key) do
|
|
case params do
|
|
%{^key => value} when is_atom(value) ->
|
|
Map.put(params, key, Worker.to_string(value))
|
|
|
|
_ ->
|
|
params
|
|
end
|
|
end
|
|
|
|
defp merge_opts(params, schema) do
|
|
case Map.split(params, @allowed_opts) do
|
|
{opts, params} when map_size(opts) > 0 ->
|
|
opts = Map.new(opts, fn {key, val} -> {to_string(key), val} end)
|
|
|
|
Map.put(params, :opts, Map.merge(schema.opts, opts))
|
|
|
|
_ ->
|
|
params
|
|
end
|
|
end
|
|
|
|
# Validators
|
|
|
|
defp expression_validator(:expression, expression) do
|
|
Expression.parse!(expression)
|
|
|
|
[]
|
|
rescue
|
|
ArgumentError ->
|
|
[expression: "expected cron expression to be a parsable binary"]
|
|
end
|
|
|
|
defp opts_validator(:opts, opts) do
|
|
string_keys = Enum.map(@allowed_opts, &to_string/1)
|
|
|
|
if Enum.all?(opts, fn {key, _} -> to_string(key) in string_keys end) do
|
|
[]
|
|
else
|
|
[opts: "expected cron opts to be one of #{inspect(@allowed_opts)}"]
|
|
end
|
|
end
|
|
end
|