AshIntrospection.Rpc.Pipeline (AshIntrospection v0.3.0)

View Source

Language-agnostic four-stage RPC pipeline for Ash actions.

Implements the core pipeline stages:

  1. parse_request/3 - Parse and validate input with fail-fast
  2. execute_ash_action/2 - Execute Ash operations
  3. process_result/3 - Apply field selection
  4. format_output/3 - Format for client consumption

Configuration

The pipeline is configured via a config map that provides all the language-specific behavior through callbacks:

%{
  input_field_formatter: :camel_case,
  output_field_formatter: :camel_case,
  field_names_callback: :interop_field_names,
  get_original_field_name: fn resource, client_key -> ... end,
  format_field_for_client: fn field_name, resource, formatter -> ... end,
  discover_action: fn otp_app, params -> ... end,
  not_found_error?: true
}

Usage

Language-specific wrappers (e.g., AshTypescript.Rpc.Pipeline) should:

  1. Build the config map with their specific callbacks
  2. Call the shared pipeline functions with that config
  3. Handle any language-specific pre/post processing

This allows each language generator to customize the behavior while sharing the core pipeline logic.

Summary

Functions

Stage 2: Execute Ash action using the parsed request.

Stage 4: Format output for client consumption.

Stage 4: Format output for client consumption with type awareness.

Formats a sort string by converting field names from client format to internal format.

Stage 3: Filter result fields using the extraction template.

Types

config()

@type config() :: %{
  optional(:input_field_formatter) => atom(),
  optional(:output_field_formatter) => atom(),
  optional(:field_names_callback) => atom(),
  optional(:get_original_field_name) => (module(), String.t() -> atom() | nil),
  optional(:format_field_for_client) => (atom(), module() | nil, atom() ->
                                           String.t()),
  optional(:not_found_error?) => boolean()
}

Functions

execute_ash_action(request, config \\ %{})

@spec execute_ash_action(AshIntrospection.Rpc.Request.t(), config()) ::
  {:ok, term()} | {:error, term()}

Stage 2: Execute Ash action using the parsed request.

Builds the appropriate Ash query/changeset and executes it. Returns the raw Ash result for further processing.

format_output(filtered_result, config \\ %{})

@spec format_output(term(), config()) :: term()

Stage 4: Format output for client consumption.

Applies output field formatting and final response structure.

The error clause covers failures raised before a %Request{} exists — action discovery, identity resolution, parameter validation. It formats them exactly as format_output_with_request/3 does, so both entry points hand the client the same response shape and the same client-resolvable placeholders.

format_output_with_request(filtered_result, request, config \\ %{})

@spec format_output_with_request(term(), AshIntrospection.Rpc.Request.t(), config()) ::
  term()

Stage 4: Format output for client consumption with type awareness.

Applies type-aware output field formatting and final response structure.

format_sort_string(sort_string, formatter)

Formats a sort string by converting field names from client format to internal format.

Handles Ash.Query.sort_input format:

  • "name" or "+name" (ascending)
  • "++name" (ascending with nils first)
  • "-name" (descending)
  • "--name" (descending with nils last)
  • "-name,++title" (multiple fields with different modifiers)

Preserves sort modifiers while converting field names using the input formatter.

Examples

iex> format_sort_string("--startDate,++insertedAt", :camel_case)
"--start_date,++inserted_at"

iex> format_sort_string("-userName", :camel_case)
"-user_name"

iex> format_sort_string(nil, :camel_case)
nil

process_result(ash_result, request, config \\ %{})

@spec process_result(term(), AshIntrospection.Rpc.Request.t(), config()) ::
  {:ok, term()} | {:error, term()}

Stage 3: Filter result fields using the extraction template.

Applies field selection to the Ash result using the pre-computed template. Performance-optimized single-pass filtering. Handles metadata extraction for both read and mutation actions.