# `Malla.Plugins.Status`
[🔗](https://github.com/netkubes/malla/blob/main/lib/malla/plugins/status.ex#L21)

Plugin that provides status and error response management with extensible callbacks.

This plugin works with `Malla.Status` to provide a complete status handling system.
To use the system, include this plugin in your service's plugin list, and use
`Malla.Status.status/2` or `Malla.Status.public/2` to convert statuses.

This plugin provides default implementations for the the following callbacks:
* `c:status/1`
* `c:status_metadata/1`
* `c:status_public/1`

See `Malla.Status` for details.

# `span_id`

```elixir
@type span_id() :: String.t() | [atom()]
```

# `status_return`

```elixir
@type status_return() :: String.t() | [Malla.Status.status_opt()] | :cont
```

# `status`
*optional* 

```elixir
@callback status(Malla.Status.user_status()) :: status_return()
```

Converts a status term into a standardized status description.

This callback receives any term representing a status or error and returns
either a status description or `:cont` to delegate to the next plugin.

## Return Values

The callback can return:

1. **Keyword list** with status description options:
   - `:info` (string) - Human-readable message (becomes `info` field)
   - `:code` (integer) - Numeric code, typically HTTP status code
   - `:status` (string/atom) - Override the status identifier
   - `:data` (map/keyword) - Additional structured data
   - `:metadata` (map/keyword) - Service metadata

2. **String** - Sets the `info` field directly (simple case)

3. **`:cont`** - Skip this handler and try the next plugin

## Examples

    # Simple message
    defcb status(:not_found), do: [info: "Resource not found", code: 404]

    # With data
    defcb status({:invalid_field, field}),
      do: [info: "Invalid field", code: 400, data: [field: field]]

    # String return
    defcb status(:timeout), do: "Operation timed out"

    # Continue to next plugin
    defcb status(_), do: :cont

Remember to always include a catch-all clause returning `:cont` to allow other plugins to handle unknown statuses.

## Implemented Status Codes

This base implementation provides out-of-the-box a number of status patterns
that are converted to statuses, but you can implement it too to support
new ones or override the default ones.

| Status Pattern | HTTP Code | Message |
|---------------------|-----------|---------|
| `:ok` | 200 | Success |
| `{:ok_data, data}` | 200 | Success (with data) |
| `:created` | 201 | Created |
| `:deleted` | 200 | Deleted |
| `:normal_termination` | 200 | Normal termination |
| `:redirect` | 307 | Redirect |
| `:bad_request` | 400 | Bad Request |
| `:content_type_invalid` | 400 | Content type is invalid |
| `{:field_invalid, field}` | 400 | Field is invalid |
| `{:field_missing, field}` | 400 | Field is missing |
| `{:field_unknown, field}` | 400 | Field is unknown |
| `:file_too_large` | 400 | File too large |
| `:invalid_parameters` | 400 | Invalid parameters |
| `{:parameter_invalid, param}` | 400 | Invalid parameter |
| `{:parameter_missing, param}` | 400 | Missing parameter |
| `{:request_op_unknown, op}` | 400 | Request OP unknown |
| `:request_body_invalid` | 400 | The request body is invalid |
| `{:syntax_error, field}` | 400 | Syntax error |
| `:token_invalid` | 400 | Token is invalid |
| `:token_expired` | 400 | Token is expired |
| `:unauthorized` | 401 | Unauthorized |
| `:forbidden` | 403 | Forbidden |
| `:not_found` | 404 | Not found |
| `:resource_invalid` | 404 | Invalid resource |
| `{:method_not_allowed, method}` | 405 | Method not allowed |
| `:verb_not_allowed` | 405 | Verb is not allowed |
| `:timeout` | 408 | Timeout |
| `:conflict` | 409 | Conflict |
| `:not_allowed` | 409 | Not allowed |
| `:service_not_found` | 409 | Service not found |
| `{:service_not_found, service}` | 409 | Service not found (specific) |
| `:gone` | 410 | Gone |
| `:unprocessable` | 422 | Unprocessable |
| `:internal_error` | 500 | Internal error |
| `{:internal_error, ref}` | 500 | Internal error (with ref) |
| `:service_not_available` | 500 | RPC service unavailable |
| `:not_implemented` | 501 | Not implemented |
| `{:service_not_available, service}` | 503 | Service not available (specific) |

# `status_metadata`
*optional* 

```elixir
@callback status_metadata(Malla.Status.t()) :: Malla.Status.t()
```

Adds or modifies metadata in a generated status structure.

This callback is invoked after a status has been fully constructed, allowing
plugins to inject additional metadata based on the service context or status content.

## Examples

    # Conditional metadata based on status
    defcb status_metadata(%{status: "internal_error"} = status) do
      metadata = Map.put(status.metadata, "support_contact", "support@example.com")
      %{status | metadata: metadata}
    end

    # Add service information to metadata
    defcb status_metadata(status) do
      metadata = Map.merge(status.metadata, %{
        "service" => "MyService",
        "version" => "1.0.0",
        "node" => to_string(node())
      })
      %{status | metadata: metadata}
    end

    defcb status_metadata(status), do: status

## Use Cases

- Adding service identification (name, version, node)
- Including timestamps or request IDs
- Adding environment information (production, staging, etc.)
- Injecting support contact information for errors
- Adding trace IDs for distributed tracing

# `status_public`
*optional* 

```elixir
@callback status_public(Malla.Status.user_status()) :: Malla.Status.t() | :cont
```

Handles unmatched statuses when using `Malla.Status.public/1`.

This callback is invoked when `public/2` encounters a status that has no
matching `status/1` callback implementation. It allows services to customize
how internal errors are exposed to external consumers.

By default, this callback:
1. Generates a unique reference number
2. Logs a warning with the full status details and reference
3. Returns a sanitized status struct with "internal_error" and the reference

## Examples

    # Default behavior (log and hide details)
    defcb status_public(user_status) do
      ref = rem(:erlang.phash2(:erlang.make_ref()), 10000)
      Logger.warning("Internal error: #{inspect(ref)}: #{inspect(user_status)} (#{srv_id})")
      %Malla.Status{
        status: "internal_error",
        info: "Internal reference #{ref}",
        code: 500
      }
    end

    defcb status_public(user_status) do
      # Fall back to default for everything else
      :cont
    end

## Use Cases

- Customizing which errors are safe to expose externally
- Adding custom logging or monitoring for unhandled statuses
- Providing different sanitization strategies per service
- Routing certain error patterns to external error tracking services

---

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