Skip to content

Instantly share code, notes, and snippets.

@slashdotdash
Created June 2, 2021 13:16
Show Gist options
  • Save slashdotdash/3fe2dbcac4193f3bcd42a14f14188091 to your computer and use it in GitHub Desktop.
Save slashdotdash/3fe2dbcac4193f3bcd42a14f14188091 to your computer and use it in GitHub Desktop.
defmodule InvalidEvent do
@doc """
Module to be used when an event cannot be deserialized from the event store.
The payload field will be populated with the source event data if
deserialization is possible.
Receiving this event usually indicates an event has been removed or renamed in
the source code between releases. Deserializing to this invalid event module
allows the application to continue running, otherwise it would terminate as
the persisted event cannot be deserialized to a valid or existing Elixir
struct.
"""
defstruct [:payload]
end
defmodule JsonSerializer do
@moduledoc """
A serializer that uses the JSON format.
Events that cannot be deserialized, due to missing event module, will be
replaced by `InvalidEvent` which includes a payload field containing the
original event data.
"""
require Logger
alias Commanded.Serialization.JsonDecoder
@doc """
Serialize given term to JSON binary data.
"""
def serialize(term) do
Jason.encode!(term)
end
@doc """
Deserialize given JSON binary data to the expected type.
"""
def deserialize(binary, config \\ []) do
{type, opts} =
case Keyword.get(config, :type) do
nil -> {nil, []}
type -> {TypeProvider.to_struct(type), [keys: :atoms]}
end
case decode(binary, type, opts) do
{:ok, event} ->
event
:error ->
decode_invalid_event(binary, opts)
end
end
# Attempt to deserialize as an invalid event.
defp decode_invalid_event(binary, opts) do
case decode("{\"payload\": #{binary}}", %InvalidEvent{}, opts) do
{:ok, %InvalidEvent{} = event} ->
event
:error ->
# Decoding the source event as the invalid event payload failed, just
# return an empty `InvalidEvent` struct
%InvalidEvent{}
end
end
defp decode(binary, type, opts) do
try do
decoded =
binary
|> Jason.decode!(opts)
|> to_struct(type)
|> JsonDecoder.decode()
{:ok, decoded}
rescue
error ->
Logger.error(fn ->
"Failed to deserialize event #{inspect(binary)} due to: " <> inspect(error)
end)
:error
end
end
defp to_struct(data, nil), do: data
defp to_struct(data, struct), do: struct(struct, data)
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment