# `PhoenixGenApi.Structs.FunConfig`
[🔗](https://github.com/ohhi-vn/phoenix_gen_api/blob/main/lib/phoenix_gen_api/structs/fun_config.ex#L1)

Defines the configuration for a function that can be called through the API.

This struct holds all the necessary information to route, validate, and execute
a function call based on an incoming request.

## Retry

The `retry` field configures retry behavior when request execution fails
(returns `{:error, _}` or `{:error, _, _}`).

Possible values:

- `nil` - No retry (default, backward compatible)
- A positive number (e.g., `3`) - Equivalent to `{:all_nodes, 3}`.
  Retry across all available nodes.
- `{:same_node, positive_number}` (e.g., `{:same_node, 2}`) - Retry on the
  same node(s) that were originally selected by the `choose_node_mode` strategy.
  Useful when the failure might be transient.
- `{:all_nodes, positive_number}` (e.g., `{:all_nodes, 3}`) - Retry across
  all available nodes in the cluster. Useful when a node might be down.

For `nodes: :local`, both `:same_node` and `:all_nodes` retry on the same
local machine since there's only one node.

Use `normalize_retry/1` to convert a raw config value to the standard tuple
format. Zero, negative numbers, strings, and other formats are invalid.

## Security Considerations

- MFA tuples are validated to ensure modules are loaded and functions exist
- Node lists are validated to prevent routing to invalid destinations
- Permission modes are checked against available request fields
- Timeout values are bounded to prevent resource exhaustion

# `t`

```elixir
@type t() :: %PhoenixGenApi.Structs.FunConfig{
  after_execute: {module(), atom()} | {module(), atom(), args :: list()} | nil,
  arg_orders: [String.t()] | :map,
  arg_types: map() | nil,
  before_execute: {module(), atom()} | {module(), atom(), args :: list()} | nil,
  check_permission:
    false | :any_authenticated | {:arg, String.t()} | {:role, list()},
  choose_node_mode: :random | :hash | {:hash, String.t()} | :round_robin,
  disabled: boolean(),
  mfa: {module(), function(), args :: list()},
  nodes: [atom()] | {module(), function(), args :: list()} | :local,
  permission_callback: {module(), atom(), args :: list()} | nil,
  request_info: boolean(),
  request_type: String.t(),
  response_type: :sync | :async | :stream | :none,
  retry: {:same_node, number()} | {:all_nodes, number()} | number() | nil,
  service: atom() | String.t(),
  timeout: integer() | :infinity,
  version: String.t()
}
```

# `check_permission!`

Checks if the request has the necessary permissions to be executed.

# `convert_args!`

Validates and converts the request arguments based on the `arg_types` and `arg_orders` configuration.

# `get_node`

Selects a target node for the request based on the `choose_node_mode` strategy.

# `is_local_service?`

Checks if the service is configured to run locally.

# `normalize_retry`

```elixir
@spec normalize_retry(
  nil
  | number()
  | {:same_node, number()}
  | {:all_nodes, number()}
) ::
  nil | {:same_node, pos_integer()} | {:all_nodes, pos_integer()}
```

Normalizes the retry configuration into a standard tuple format.

- `nil` remains `nil` (no retry)
- A number `n` is converted to `{:all_nodes, n}`
- `{:same_node, n}` and `{:all_nodes, n}` are returned as-is

## Examples

    iex> FunConfig.normalize_retry(nil)
    nil

    iex> FunConfig.normalize_retry(3)
    {:all_nodes, 3}

    iex> FunConfig.normalize_retry({:same_node, 2})
    {:same_node, 2}

    iex> FunConfig.normalize_retry({:all_nodes, 5})
    {:all_nodes, 5}

# `valid?`

Validates the function configuration.

Returns `true` if all configuration fields are valid, `false` otherwise.
Logs detailed error messages for each invalid field.

## Validation Checks

- `request_type` must be a non-empty string
- `service` must not be nil
- `nodes` must be a valid list, MFA tuple, or `:local`
- `choose_node_mode` must be a recognized strategy
- `timeout` must be a positive integer or `:infinity`
- `mfa` must be a valid `{module, function, args}` tuple
- `arg_types` and `arg_orders` must be consistent
- `response_type` must be one of `:sync`, `:async`, `:stream`, or `:none`
- `check_permission` must be a valid permission mode
- `request_info` must be a boolean

# `validate_with_details`

Validates the function configuration and returns detailed error information.

Returns `{:ok, config}` if valid, or `{:error, [error_messages]}` if invalid.

# `version`

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

Returns the version of the function configuration.
If the version is not set or missing (for backward compatibility with old configs), returns "0.0.0" as default.

---

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