defmodule Grapix.GraphqlWS.V1.JSONSerializer do @moduledoc false @behaviour Phoenix.Socket.Serializer alias Phoenix.Socket.{Broadcast, Message, Reply} @impl true def fastlane!(%Broadcast{} = msg) do data = phoenix2gql(nil, "1", msg.topic, msg.event, msg.payload.result) {:socket_push, :text, Phoenix.json_library().encode_to_iodata!(data)} end @impl true def encode!(%Reply{payload: %{ subscriptionId: _subscription_id }} = reply) do data = phoenix2gql(reply.join_ref, reply.ref, reply.topic, "subs_ack", reply.payload) # client doesn't like empty or unexpectedly-shapen data, but fine with nought #{:socket_push, :text, Phoenix.json_library().encode_to_iodata!(data)} {nil, :text, Phoenix.json_library().encode_to_iodata!(data)} end def encode!(%Reply{payload: %{}} = reply) do data = case reply.status do :error -> phoenix2gql(reply.join_ref, reply.ref, reply.topic, "phx_error", reply.payload) :ok -> phoenix2gql(reply.join_ref, reply.ref, reply.topic, "phx_reply", nil) end {:socket_push, :text, Phoenix.json_library().encode_to_iodata!(data)} end def encode!(%Message{} = msg) do data = phoenix2gql(msg.join_ref, msg.ref, msg.topic, "phx_message", msg.payload) {:socket_push, :text, Phoenix.json_library().encode_to_iodata!(data)} end defmodule(RandomId, do: use(Puid, bits: 64, charset: :alphanum_lower)) @impl true def decode!(raw_message, _opts) do incoming = raw_message |> Phoenix.json_library().decode! Map.merge(%{ "payload" => %{}, "id" => RandomId.generate()}, incoming) |> gql2Phoenix end defp gql2Phoenix(%{ "type" => "connection_init", "payload" => payload, "id" => id }) do %Phoenix.Socket.Message{ topic: "__absinthe__:control", event: "phx_join", payload: payload, ref: id, join_ref: nil } end defp gql2Phoenix(%{ "type" => "start", "payload" => payload, "id" => id }) do variables = Map.put(Map.get(payload, "variables", %{}), :__graphql_client_ref__, id) %Phoenix.Socket.Message{ topic: "__absinthe__:control", event: "doc", payload: Map.put(payload, "variables", variables), ref: id, join_ref: nil } end defp gql2Phoenix(%{ "type" => "stop", "payload" => payload, "id" => id }) do %Phoenix.Socket.Message{ topic: "__absinthe__:control", event: "phx_leave", payload: payload, ref: nil, join_ref: id } end #[msg.join_ref, msg.ref, msg.topic, msg.event, msg.payload] defp phoenix2gql(_join_ref, ref, "__absinthe__:control", "subs_ack", %{subscriptionId: _subscription_id} = payload) do %{ "type" => "data", "id" => ref, "payload" => %{data: payload}} end defp phoenix2gql(join_ref, _ref, "__absinthe__:control", "phx_reply", payload) do %{ "type" => "connection_ack", "id" => join_ref, "payload" => payload } end defp phoenix2gql(_, ref, "__absinthe__:control", "phx_message", payload) do %{ "type" => "data", "id" => ref, "payload" => payload } end defp phoenix2gql(_, ref, "__absinthe__:control", "phx_error", payload) do %{ "type" => "error", "id" => ref, "payload" => payload } end defp phoenix2gql(_, ref, _msg_topic, "subscription:data", payload) do %{ "type" => "data", "id" => ref, "payload" => payload } end end