Phoenix.Sync.Writer.Format behaviour (Phoenix.Sync v0.4.3)

View Source

Defines a behaviour that applications can implement in order to handle custom data formats within a Phoenix.Sync.Writer

The exact format of the change messages coming from the client is unimportant, however Phoenix.Sync.Writer requires certain essential information that needs to be included.

  • operation - one of insert, update or delete
  • relation - the table name that the operation applies to.
  • data - the original data before the mutation was applied. Required for update or delete operations
  • changes - the changes to apply. Required for insert and update operations

However you choose to encode this information on the client, you simply need to set the format of your write configuration using to a module that implements this behaviour.

Implementation guide

Once you have parsed the data coming from the client, over HTTP or even raw TCP, use Phoenix.Sync.Writer.Operation.new/4 (or Phoenix.Sync.Writer.Transaction.operation/4) to validate the values and create a %Phoenix.Sync.Writer.Operation{} struct.

Phoenix.Sync.Writer.Transaction.parse_operations/2 is a helper function for error handling when mapping a list of encoded operation data using Phoenix.Sync.Writer.Transaction.operation/4.

Example:

We use Protocol buffers to encode the incoming information using :protox and implement this module's parse_transaction/1 callback using the Protox decode functions:

defmodule MyApp.Protocol do
  use Protox,
    namespace: __MODULE__,
    schema: ~S[
      syntax: "proto2";
      message Operation {
        enum Op {
          INSERT = 0;
          UPDATE = 1;
          DELETE = 2;
        }
        required Op op = 1;
        string table = 2;
        map<string, string> original = 3;
        map<string, string> modified = 4;
      }
      message Transaction {
        repeated Operation operations = 1;
      }
    ]

  alias Phoenix.Sync.Writer

  @behaviour Phoenix.Sync.Writer.Format

  @impl Phoenix.Sync.Writer.Format
  def parse_transaction(proto) when is_binary(proto) do
    with {:ok, %{operations: ops}} <- MyApp.Protocol.Transaction.decode(proto),
         {:ok, operations} <- Writer.Transaction.parse_operations(ops, &convert_operation/1) do
      {:ok, %Writer.Transaction{operations: operations}
    end
  end

  defp convert_operation(%MyApp.Protocol.Operation{} = operation) do
    Writer.Transaction.operation(
      operation.op,
      operation.table,
      operation.original,
      operation.modified
    )
  end
end

We use our protocol module as the format when we apply/4 our transaction data and we can just pass our serialized protobuf message as the mutation data:

{:ok, txid, _changes} =
  Phoenix.Sync.Writer.new()
  |> Phoenix.Sync.Writer.apply(protobuf_data, MyApp.Repo, format: MyApp.Protocol)

Summary

Types

t()

Raw data from a client that will be parsed to a Phoenix.Sync.Writer.Format.Transaction by the Writer's format parser

Callbacks

Translate some data format into a Phoenix.Sync.Writer.Transaction with a list of operations to apply.

Types

parse_transaction_result()

@type parse_transaction_result() ::
  {:ok, Phoenix.Sync.Writer.Transaction.t()} | {:error, term()}

parser_fun()

@type parser_fun() :: (transaction_data() -> parse_transaction_result()) | mfa()

t()

@type t() :: module()

transaction_data()

@type transaction_data() :: term()

Raw data from a client that will be parsed to a Phoenix.Sync.Writer.Format.Transaction by the Writer's format parser

Callbacks

parse_transaction(term)

@callback parse_transaction(term()) :: parse_transaction_result()

Translate some data format into a Phoenix.Sync.Writer.Transaction with a list of operations to apply.