# Workers

Use `Oban.Pro.Worker` with `args_schema` for type-safe, validated job arguments:

```elixir
defmodule MyApp.SomeWorker do
  use Oban.Pro.Worker, queue: :default, max_attempts: 3

  args_schema do
    field :user_id, :integer, required: true
    field :status, :enum, values: ~w(pending active)a, default: :pending
    field :metadata, :map
  end

  @impl Oban.Pro.Worker
  def process(%Oban.Job{args: %__MODULE__{} = args}) do
    :ok
  end
end
```

## Structured Args

Validates data when calling `new/1`, before insertion. Notable types:

- `:enum` - Validates and casts to atoms: `field :status, :enum, values: ~w(pending completed)a`
- `:term` - Safely encodes any Elixir term
- `:uuid` - Alias for `:binary_id`
- `{:array, *}` - Lists of typed values

Nested structures with `embeds_one` and `embeds_many`:

```elixir
args_schema do
  field :order_id, :integer, required: true
  embeds_one :customer, required: true do
    field :customer_id, :integer, required: true
  end
  embeds_many :line_items do
    field :product_id, :integer, required: true
    field :quantity, :integer, required: true
  end
end
```

## Encrypted jobs

Protect sensitive args with transparent encryption:

```elixir
use Oban.Pro.Worker,
  queue: :default,
  encrypted: [key: {Application, :fetch_env!, [:my_app, :oban_key]}]
```

**Important:** Only `args` are encrypted, never put sensitive data in `meta`.

## Hooks

**Execution hooks:**

- `before_new/2` — modify args/opts before job creation
- `before_process/1` — runs before `process/1`
- `after_process/3` — runs after `process/1` with state (`:complete`, `:cancel`, `:discard`, `:error`, `:snooze`), job, and result

**External state hooks:**

- `on_cancelled/2` — job cancelled externally (manual, deadline, dependency)
- `on_discarded/2` — job discarded by DynamicLifeline

```elixir
@impl Oban.Pro.Worker
def after_process(state, job, result) do
  if state in [:error, :discard], do: notify_error(job)
  :ok
end
```

Attach hooks explicitly or globally:

```elixir
use Oban.Pro.Worker, hooks: [MyApp.ErrorHook]

# Global hooks (in application startup)
Oban.Pro.Worker.attach_hook(:errors, MyApp.ErrorHook)
```

## Deadlines

Cancel jobs that don't run within a time limit:

```elixir
use Oban.Pro.Worker, deadline: {1, :hour}

# Override at runtime
MyWorker.new(%{}, deadline: {30, :minutes})

# Force cancel already-running jobs
use Oban.Pro.Worker, deadline: [in: {10, :minutes}, force: true]
```
