Skip to content

Instantly share code, notes, and snippets.

@vheathen
Created February 20, 2018 20:38
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save vheathen/92eed86ca25052b007535c49c1507d88 to your computer and use it in GitHub Desktop.
Save vheathen/92eed86ca25052b007535c49c1507d88 to your computer and use it in GitHub Desktop.
Channel's injectable module example
defmodule ProfileWeb.CustomerChannel.Step2 do
@moduledoc """
This module adds the application step 2 handlers to the channel
"""
defmacro __before_compile__(_env) do
quote do
def proceed_incoming("step2",
payload,
%{assigns: %{
connection: %{last_step: 1},
passport: passport_data
}} = socket)
when not is_nil(passport_data)
do
handle_in("garbage", "", socket)
end
def proceed_incoming("step2", payload, %{assigns: %{connection: %{last_step: 1}}} = socket) do
case passport_format_valid?(payload) do
true ->
socket = assign(socket, :passport, payload)
send(self(), {:validate_passport, payload})
{:reply, {:ok, %{}}, socket}
_ ->
handle_in("garbage", "", socket)
end
end
# validate passport via DaData
def handle_info({:validate_passport, %{"series" => series, "number" => number}}, socket) do
passport = "#{series} #{number}"
host_pid = self()
task_pid = Task.start(fn ->
send(host_pid, {:passport_validation_result,
DaData.Clean.passport(passport),
passport})
end)
# validation timeout
Process.send_after(self(), {:passport_validation_timeout, task_pid, passport}, 10_000)
{:noreply, socket}
end
def handle_info({:passport_validation_timeout, task_pid, passport}, socket)
when is_pid(task_pid)
do
if Process.alive?(task_pid) do
Process.exit(task_pid, :timeout)
send(self(), {:passport_validation_result, :error, passport})
end
{:noreply, socket}
end
def handle_info({:passport_validation_timeout, _, _}, socket) do
{:noreply, socket}
end
# qc = 0, valid password
def handle_info({:passport_validation_result, [%{"qc" => 0, "source" => source}], _},
%{assigns:
%{passport: %{"series" => series, "number" => number}}
} = socket)
do
socket =
if source == "#{series} #{number}", do: send_passport_to_backend(socket),
else: socket
{:noreply, socket}
end
# any other qc = something wrong with passport data
def handle_info({:passport_validation_result, [%{"qc" => _, "source" => source}], _},
%{assigns:
%{passport: %{"series" => series, "number" => number}}
} = socket)
do
socket =
if source == "#{series} #{number}" do
push socket, "passport invalid", %{"series" => series, "number" => number}
socket
|> wipe_passport()
|> assign(:passport_entry_error, true)
else
socket
end
{:noreply, socket}
end
# Passport validation other (general) errors
def handle_info({:passport_validation_result, _, passport},
%{assigns:
%{passport: %{"series" => series, "number" => number}}
} = socket)
do
socket =
if passport == "#{series} #{number}" do
socket
|> assign(:passport_dadata_error, true)
|> send_passport_to_backend()
else
socket
end
{:noreply, socket}
end
def handle_info({:passport_validation_result, _, _}, socket) do
{:noreply, socket}
end
# send to backend - with errors
defp send_passport_to_backend(%{assigns: %{
passport: assigned_passport,
passport_dadata_error: true,
connection: %{customer_id: customer_id}
}} = socket)
do
socket
|> assign(:passport, Map.put(assigned_passport, "passport_dadata_error", true))
|> assign(:passport_dadata_error, nil)
|> send_passport_to_backend()
end
defp send_passport_to_backend(%{assigns: %{
passport: assigned_passport,
passport_entry_error: true,
connection: %{customer_id: customer_id}
}} = socket)
do
socket
|> assign(:passport, Map.put(assigned_passport, "passport_entry_error", true))
|> assign(:passport_entry_error, nil)
|> send_passport_to_backend()
end
# send to backend
defp send_passport_to_backend(%{assigns: %{
passport: %{"living_address" => %{"data" => address}} = passport,
connection: %{customer_id: customer_id}
}} = socket)
do
socket = send_application_to_backend("step2", passport, socket)
%{region: region, city: city} = Profile.Accounts.update_living_data(customer_id, address)
Profile.Accounts.set_last_step(customer_id, 2)
socket = update_in(socket.assigns.connection, &Map.put(&1, :last_step, 2))
socket.endpoint.broadcast "customer:#{socket.assigns.connection.customer_id}", "step2 complete", %{"region" => region, "city" => city}
wipe_passport(socket)
end
defp wipe_passport(socket) do
update_in(socket.assigns, &Map.delete(&1, :passport))
end
defp passport_format_valid?(
%{
"series" => series,
"number" => number,
"issue_date" => issue_date,
"issue_division" => issue_division,
"issue_code" => issue_code,
"birth_place" => birth_place,
"reg_address" => %{"value" => reg_address},
"living_address" => %{"value" => living_address}
})
when
is_binary(series) and byte_size(series) == 4
and is_binary(number) and byte_size(number) == 6
and is_binary(issue_date) and byte_size(issue_date) >= 10
and is_binary(issue_division) and byte_size(issue_division) > 0
and is_binary(issue_code) and byte_size(issue_code) == 7
and is_binary(birth_place) and byte_size(birth_place) > 0
and is_binary(reg_address) and byte_size(reg_address) > 0
and is_binary(living_address) and byte_size(living_address) > 0,
do:
true
defp passport_format_valid?(_), do: false
end
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment