ExESDBGater.MessageHelpers (ex_esdb_gater v0.8.0)

Common utilities for ExESDBGater message creation, validation, and handling.

This module provides consistent patterns for all message modules to ensure:

  • Proper node field tracking
  • Standardized timestamp handling
  • Message validation
  • Consistent defaults and patterns

Node Field Guidelines

All message structs should include a node-related field that tracks where the event originated:

  • :node - for general events originating on a node
  • :originating_node - for cluster events where distinction is needed from affected nodes
  • :reporting_node - for metrics/monitoring events where the reporter may differ from the source

Usage

# In message creation functions
def my_message(arg1, arg2, opts \ []) do
  %MyMessage{
    arg1: arg1,
    arg2: arg2,
    node: MessageHelpers.get_node(opts),
    timestamp: MessageHelpers.current_timestamp()
  }
end

# Validate message has required fields
MessageHelpers.validate_message_fields(message, [:node, :timestamp])

Summary

Functions

Extracts cluster context information for enhanced message tracking.

Returns the current timestamp with millisecond precision in UTC.

Ensures consistent metadata structure across all messages.

Creates a unique identifier for messages that need tracking.

Gets the node value from options, defaulting to Node.self().

Gets the originating_node value from options, defaulting to Node.self().

Gets the reporting_node value from options, defaulting to Node.self().

Common validation for all message structs.

Validates that a message struct has all required fields.

Validates that all node-related fields in a message are atoms.

Functions

cluster_context()

Extracts cluster context information for enhanced message tracking.

This provides additional context that can be useful for debugging and monitoring in distributed environments.

Returns

A map containing:

  • :node_name - Current node name
  • :connected_nodes - List of connected nodes
  • :cluster_size - Number of nodes in cluster
  • :node_type - Derived node type (if determinable)

current_timestamp()

Returns the current timestamp with millisecond precision in UTC.

This ensures consistent timestamp formatting across all message types.

enrich_metadata(metadata \\ %{})

Ensures consistent metadata structure across all messages.

Merges provided metadata with standard cluster context information.

Examples

iex> MessageHelpers.enrich_metadata(%{custom: "value"})
%{
  custom: "value",
  cluster_context: %{node_name: :node@host, cluster_size: 3, ...}
}

generate_message_id(prefix \\ "msg")

Creates a unique identifier for messages that need tracking.

Generates a short, URL-safe identifier that includes timestamp info for better traceability.

get_node(opts \\ [])

Gets the node value from options, defaulting to Node.self().

Examples

iex> MessageHelpers.get_node([])
:"node@hostname"

iex> MessageHelpers.get_node([node: :test_node])
:test_node

get_originating_node(opts \\ [])

Gets the originating_node value from options, defaulting to Node.self().

Used for cluster membership and similar events where the originating node needs to be distinguished from affected nodes.

get_reporting_node(opts \\ [])

Gets the reporting_node value from options, defaulting to Node.self().

Used for metrics and monitoring events where the reporting node may be different from the event source.

validate_common(message, required_fields)

Common validation for all message structs.

This is a comprehensive validation that checks:

  • Required fields are present
  • Node fields are valid atoms
  • Timestamp is a valid DateTime

Usage in message modules

def validate(%MyMessage{} = message) do
  MessageHelpers.validate_common(message, [:field1, :field2])
end

validate_message_fields(message, required_fields)

Validates that a message struct has all required fields.

Parameters

  • message - The message struct to validate
  • required_fields - List of atoms representing required field names

Examples

iex> message = %MyMessage{node: :test, timestamp: DateTime.utc_now()}
iex> MessageHelpers.validate_message_fields(message, [:node, :timestamp])
{:ok, message}

iex> message = %MyMessage{timestamp: DateTime.utc_now()}
iex> MessageHelpers.validate_message_fields(message, [:node, :timestamp])
{:error, {:missing_fields, [:node]}}

validate_node_fields(message)

Validates that all node-related fields in a message are atoms.

Examples

iex> message = %{node: :test_node, originating_node: :origin}
iex> MessageHelpers.validate_node_fields(message)
:ok

iex> message = %{node: "invalid_node"}
iex> MessageHelpers.validate_node_fields(message)
{:error, {:invalid_node_fields, [:node]}}