AshTypescript.FieldFormatter (ash_typescript v0.18.0)

Copy Markdown View Source

Handles field name formatting for input parameters, output fields, and TypeScript generation.

Supports built-in formatters and custom formatter functions.

Summary

Functions

Formats a field name for client output, optionally applying resource/type-level field_names mapping.

Formats a field name using the configured formatter.

Formats a map of fields, converting all keys using the specified formatter.

Recursively formats all keys in a nested structure for client consumption.

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

Parses input field names from client format to internal format.

Parses a map of input fields, converting all keys from client format to internal format.

Recursively parses input values, handling nested structures.

Resolves a field name to its existing atom, applying the formatter for case conversion.

Functions

format_field_for_client(field, resource_or_type_module \\ nil, formatter)

Formats a field name for client output, optionally applying resource/type-level field_names mapping.

Use this when formatting field names for client consumption where the field might have a custom TypeScript name via the field_names DSL option or the typescript_field_names callback function.

Examples

iex> AshTypescript.FieldFormatter.format_field_for_client(:user_name, nil, :camel_case)
"userName"

iex> AshTypescript.FieldFormatter.format_field_for_client("already_string", nil, :camel_case)
"alreadyString"

When a resource or type module is provided with field_names/typescript_field_names mappings (e.g., :is_active?"isActive"), the mapped string value is used directly WITHOUT additional formatting.

format_field_name(field_name, formatter)

Formats a field name using the configured formatter.

Examples

iex> AshTypescript.FieldFormatter.format_field_name(:user_name, :camel_case)
"userName"

iex> AshTypescript.FieldFormatter.format_field_name(:user_name, :snake_case)
"user_name"

iex> AshTypescript.FieldFormatter.format_field_name("user_name", :pascal_case)
"UserName"

format_fields(fields, formatter)

Formats a map of fields, converting all keys using the specified formatter.

Examples

iex> AshTypescript.FieldFormatter.format_fields(%{user_name: "John", user_email: "john@example.com"}, :camel_case)
%{"userName" => "John", "userEmail" => "john@example.com"}

format_output_field_names(data, formatter)

Recursively formats all keys in a nested structure for client consumption.

Walks maps and lists, converting every key with the given formatter. Structs and primitives are returned untouched, and non-atom/non-binary keys are left as-is.

Used for any payload that is handed to the client without going through a type-driven formatter — RPC responses and typed controller error bodies both rely on this so their field names agree.

Examples

iex> AshTypescript.FieldFormatter.format_output_field_names(%{short_message: "x"}, :camel_case)
%{"shortMessage" => "x"}

iex> AshTypescript.FieldFormatter.format_output_field_names([%{user_name: "a"}], :camel_case)
[%{"userName" => "a"}]

format_sort_string(sort_list, 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> AshTypescript.FieldFormatter.format_sort_string("--startDate,++insertedAt", :camel_case)
"--start_date,++inserted_at"

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

iex> AshTypescript.FieldFormatter.format_sort_string(nil, :camel_case)
nil

parse_input_field(field_name, formatter)

Parses input field names from client format to internal format.

This is used for converting incoming client field names to the internal Elixir atom keys that Ash expects.

Examples

iex> AshTypescript.FieldFormatter.parse_input_field("userName", :camel_case)
:user_name

parse_input_fields(fields, formatter)

Parses a map of input fields, converting all keys from client format to internal format.

Recursively processes nested maps and arrays to ensure all field names are properly formatted. This is essential for union types and embedded resources that contain nested field structures.

Examples

iex> AshTypescript.FieldFormatter.parse_input_fields(%{"userName" => "John", "userEmail" => "john@example.com"}, :camel_case)
%{user_name: "John", user_email: "john@example.com"}

iex> AshTypescript.FieldFormatter.parse_input_fields(%{"attachments" => [%{"mimeType" => "pdf", "attachmentType" => "file"}]}, :camel_case)
%{attachments: [%{mime_type: "pdf", attachment_type: "file"}]}

parse_input_value(value, formatter)

Recursively parses input values, handling nested structures.

This function ensures that all nested maps and arrays containing maps have their field names properly formatted according to the formatter.

Only handles JSON-decoded data (maps, lists, primitives) - no structs.

resolve_field_name(field_name, formatter)

Resolves a field name to its existing atom, applying the formatter for case conversion.

Atoms are passed through unchanged. Every valid string field name corresponds to an atom that already exists (resource attributes, relationships, calculations, and aggregates are all defined at compile time), so a string resolves to an existing atom when one is available and otherwise returns the formatted string unchanged. Downstream field selection compares the result against the known field atoms, so an unresolved name simply fails as an unknown field.

This deliberately never calls String.to_atom/1: client-supplied field names are attacker-controllable, and minting a fresh atom per name would allow atom table exhaustion (a node-wide denial of service).

Examples

iex> AshTypescript.FieldFormatter.resolve_field_name("userName", :camel_case)
:user_name

iex> AshTypescript.FieldFormatter.resolve_field_name(:user_name, :camel_case)
:user_name