# `AshPhoenixGenApi.Resource.ActionConfig`
[🔗](https://github.com/ohhi-vn/ash_phoenix_gen_api/blob/v1.0.3/lib/ash_phoenix_gen_api/resource/action_config.ex#L1)

Configuration struct for a single PhoenixGenApi action endpoint.

This struct is the target of the `action` entity in the `gen_api` DSL section.
It holds all the configuration needed to generate a `PhoenixGenApi.Structs.FunConfig`
from an Ash resource action.

## Fields

- `name` - The Ash action name (required)
- `request_type` - The PhoenixGenApi request type string (defaults to action name)
- `timeout` - Timeout in milliseconds
- `response_type` - Response mode (:sync, :async, :stream, :none)
- `request_info` - Whether to pass request info as last argument
- `check_permission` - Permission check mode
- `permission_callback` - Custom callback MFA for permission checking (takes precedence over check_permission). Callback receives `(request_type, args)` and returns `true` (continue) or `false` (denied).
- `choose_node_mode` - Node selection strategy
- `nodes` - Target nodes (list, MFA tuple, or :local)
- `retry` - Retry configuration
- `version` - API version string
- `mfa` - Explicit MFA tuple (overrides auto-generated)
- `arg_types` - Explicit argument types map (overrides auto-derived)
- `arg_orders` - Explicit argument order list (overrides auto-derived), or `:map` to derive from arg_types keys (default)
- `disabled` - Whether this endpoint is disabled
- `code_interface?` - Whether to generate a code interface function for this action
- `result_encoder` - How to encode the result returned from the action MFA call

## Resolution Order

When generating a `FunConfig`, values are resolved in this order:

1. Action-level explicit configuration (e.g., `action :foo do timeout 10_000 end`)
2. Section-level defaults (e.g., `gen_api do timeout 5_000 end`)
3. Built-in defaults (e.g., timeout defaults to 5000)

For `arg_types` and `arg_orders`:
1. Explicit `arg_types`/`arg_orders` on the action entity
2. Auto-derived from the Ash action's accepted attributes and arguments

# `choose_node_mode`

```elixir
@type choose_node_mode() :: AshPhoenixGenApi.Resource.SharedTypes.choose_node_mode()
```

# `gen_api_type`

```elixir
@type gen_api_type() :: AshPhoenixGenApi.Resource.SharedTypes.gen_api_type()
```

# `node_config`

```elixir
@type node_config() :: AshPhoenixGenApi.Resource.SharedTypes.node_config()
```

# `permission_callback`

```elixir
@type permission_callback() ::
  AshPhoenixGenApi.Resource.SharedTypes.permission_callback()
```

# `permission_mode`

```elixir
@type permission_mode() :: AshPhoenixGenApi.Resource.SharedTypes.permission_mode()
```

# `result_encoder`

```elixir
@type result_encoder() :: AshPhoenixGenApi.Resource.SharedTypes.result_encoder()
```

# `retry_config`

```elixir
@type retry_config() :: AshPhoenixGenApi.Resource.SharedTypes.retry_config()
```

# `t`

```elixir
@type t() :: %AshPhoenixGenApi.Resource.ActionConfig{
  __spark_metadata__: any(),
  arg_orders: [String.t()] | :map,
  arg_types: %{required(String.t()) =&gt; gen_api_type()} | nil,
  check_permission: permission_mode() | nil,
  choose_node_mode: choose_node_mode() | nil,
  code_interface?: boolean() | nil,
  disabled: boolean(),
  mfa: {module(), atom(), [any()]} | nil,
  name: atom(),
  nodes: node_config() | nil,
  permission_callback: permission_callback(),
  request_info: boolean() | nil,
  request_type: String.t() | nil,
  response_type: :sync | :async | :stream | :none | nil,
  result_encoder: result_encoder(),
  retry: retry_config() | nil,
  timeout: pos_integer() | :infinity | nil,
  version: String.t() | nil
}
```

# `permission_callback`

```elixir
@callback permission_callback(request_type :: String.t(), args :: map()) :: boolean()
```

Callback function signature for permission checking.

The callback receives two arguments:
- `request_type` - The PhoenixGenApi request type string (e.g., `"delete_user"`)
- `args` - A map of request arguments (e.g., `%{"user_id" => "123", "role" => "admin"}`)

Returns `true` to allow the request, or `false` to deny permission.

## Example

    def check_permission(request_type, args) do
      case request_type do
        "delete_user" -> args["role"] == "admin"
        "update_profile" -> args["user_id"] == args["target_user_id"]
        _ -> true
      end
    end

# `effective_check_permission`

```elixir
@spec effective_check_permission(t(), permission_mode()) :: permission_mode()
```

Resolves the effective check_permission, falling back to the provided default.

# `effective_choose_node_mode`

```elixir
@spec effective_choose_node_mode(t(), choose_node_mode()) :: choose_node_mode()
```

Resolves the effective choose_node_mode, falling back to the provided default.

# `effective_code_interface?`

```elixir
@spec effective_code_interface?(t(), boolean()) :: boolean()
```

Resolves the effective code_interface? setting, falling back to the provided default.

When the action-level `code_interface?` is explicitly set (not `nil`), returns that value.
Otherwise, returns the section-level default.

## Examples

    iex> config = %AshPhoenixGenApi.Resource.ActionConfig{code_interface?: false}
    iex> AshPhoenixGenApi.Resource.ActionConfig.effective_code_interface?(config, true)
    false

    iex> config = %AshPhoenixGenApi.Resource.ActionConfig{code_interface?: nil}
    iex> AshPhoenixGenApi.Resource.ActionConfig.effective_code_interface?(config, true)
    true

# `effective_mfa`

```elixir
@spec effective_mfa(t(), module()) :: {module(), atom(), [any()]}
```

Resolves the effective mfa, falling back to auto-generation from the resource module and action name.

When `mfa` is explicitly set on the action config, it is returned as-is.
Otherwise, generates `{resource_module, action_name, []}`.

The generated function is expected to accept positional arguments matching
`arg_orders`, plus an optional request_info map as the last argument.

# `effective_nodes`

```elixir
@spec effective_nodes(t(), node_config()) :: node_config()
```

Resolves the effective nodes, falling back to the provided default.

# `effective_permission_callback`

```elixir
@spec effective_permission_callback(t(), permission_callback()) ::
  permission_callback()
```

Resolves the effective permission_callback, falling back to the provided default.

When the entity-level `permission_callback` is set, returns that value.
Otherwise, returns the section-level default.

The callback MFA function receives `(request_type, args)` as arguments and
returns `true` (continue) or `false` (permission denied).

## Examples

    iex> config = %Elixir.AshPhoenixGenApi.Resource.ActionConfig{permission_callback: {MyModule, :check, []}}
    iex> Elixir.AshPhoenixGenApi.Resource.ActionConfig.effective_permission_callback(config, nil)
    {MyModule, :check, []}

    iex> config = %Elixir.AshPhoenixGenApi.Resource.ActionConfig{permission_callback: nil}
    iex> Elixir.AshPhoenixGenApi.Resource.ActionConfig.effective_permission_callback(config, {MyModule, :check, []})
    {MyModule, :check, []}

    iex> config = %Elixir.AshPhoenixGenApi.Resource.ActionConfig{permission_callback: nil}
    iex> Elixir.AshPhoenixGenApi.Resource.ActionConfig.effective_permission_callback(config, nil)
    nil

# `effective_request_info`

```elixir
@spec effective_request_info(t(), boolean()) :: boolean()
```

Resolves the effective request_info, falling back to the provided default.

# `effective_request_type`

```elixir
@spec effective_request_type(t()) :: String.t()
```

Resolves the effective request_type for this action config.

Returns the explicit `request_type` if set, otherwise derives it
from the action `name` by converting to a string.

## Examples

    iex> config = %AshPhoenixGenApi.Resource.ActionConfig{name: :send_message, request_type: "send_msg"}
    iex> AshPhoenixGenApi.Resource.ActionConfig.effective_request_type(config)
    "send_msg"

    iex> config = %AshPhoenixGenApi.Resource.ActionConfig{name: :send_message, request_type: nil}
    iex> AshPhoenixGenApi.Resource.ActionConfig.effective_request_type(config)
    "send_message"

# `effective_response_type`

```elixir
@spec effective_response_type(t(), :sync | :async | :stream | :none) ::
  :sync | :async | :stream | :none
```

Resolves the effective response type, falling back to the provided default.

# `effective_result_encoder`

```elixir
@spec effective_result_encoder(t(), result_encoder()) :: result_encoder()
```

Resolves the effective result_encoder setting, falling back to the provided default.

The `result_encoder` determines how the result from the action MFA call is encoded:

- `:struct` — Return the Ash resource struct as-is (default behavior)
- `:map` — Convert the Ash resource struct to a map containing only public fields
  (using `Ash.Resource.Info.public_fields/1` to filter; falls back to
  `Map.from_struct/1` for non-Ash-resource structs)
- `{Module, :function, args}` — Custom encoder MFA. The function receives
  the result as its first argument, followed by `args`, and must return
  the encoded result.
- `nil` — Inherit from the section-level default

When the action-level `result_encoder` is explicitly set (not `nil`), returns that value.
Otherwise, returns the section-level default.

## Examples

    iex> config = %AshPhoenixGenApi.Resource.ActionConfig{result_encoder: :map}
    iex> AshPhoenixGenApi.Resource.ActionConfig.effective_result_encoder(config, :struct)
    :map

    iex> config = %AshPhoenixGenApi.Resource.ActionConfig{result_encoder: nil}
    iex> AshPhoenixGenApi.Resource.ActionConfig.effective_result_encoder(config, :struct)
    :struct

    iex> config = %AshPhoenixGenApi.Resource.ActionConfig{result_encoder: {MyEncoder, :encode, []}}
    iex> AshPhoenixGenApi.Resource.ActionConfig.effective_result_encoder(config, :struct)
    {MyEncoder, :encode, []}

# `effective_retry`

```elixir
@spec effective_retry(t(), retry_config()) :: retry_config()
```

Resolves the effective retry, falling back to the provided default.

# `effective_timeout`

```elixir
@spec effective_timeout(t(), pos_integer() | :infinity) :: pos_integer() | :infinity
```

Resolves the effective timeout, falling back to the provided default.

## Examples

    iex> config = %Elixir.AshPhoenixGenApi.Resource.ActionConfig{timeout: 10_000}
    iex> Elixir.AshPhoenixGenApi.Resource.ActionConfig.effective_timeout(config, 5_000)
    10_000

    iex> config = %Elixir.AshPhoenixGenApi.Resource.ActionConfig{timeout: nil}
    iex> Elixir.AshPhoenixGenApi.Resource.ActionConfig.effective_timeout(config, 5_000)
    5000

# `effective_version`

```elixir
@spec effective_version(t(), String.t()) :: String.t()
```

Resolves the effective version, falling back to the provided default.

# `enabled?`

```elixir
@spec enabled?(t()) :: boolean()
```

Checks if this config is enabled (not disabled).

# `has_explicit_arg_orders?`

```elixir
@spec has_explicit_arg_orders?(t()) :: boolean()
```

Checks if this config has explicit arg_orders defined (not `:map`).

# `has_explicit_arg_types?`

```elixir
@spec has_explicit_arg_types?(t()) :: boolean()
```

Checks if this config has explicit arg_types defined.

---

*Consult [api-reference.md](api-reference.md) for complete listing*
