Current section

Files

Jump to
split_thin_sdk lib split rpc response_parser.ex
Raw

lib/split/rpc/response_parser.ex

defmodule Split.RPC.ResponseParser do
@moduledoc """
Contains the functions to parse the response from the Splitd RPC calls.
"""
require Logger
use Split.RPC.Opcodes
alias Split.RPC.Fallback
alias Split.RPC.Message
alias Split.Telemetry
alias Split.Impression
alias Split.SplitView
@type splitd_response :: {:ok, map()} | {:error, term()}
@doc """
Parses the response from the Splitd RPC calls.
"""
@spec parse_response(response :: splitd_response(), request :: Message.t(), [
{:span_context, reference()} | {:span_context, nil}
]) :: map() | list() | Impression.t() | SplitView.t() | boolean() | nil
def parse_response(response, original_request, opts \\ [])
def parse_response(
{:ok, %{"s" => @status_ok, "p" => treatment_data}},
%Message{
o: opcode,
a: args
},
_opts
)
when opcode in [@get_treatment_opcode, @get_treatment_with_config_opcode] do
key = Enum.at(args, 0)
bucketing_key = Enum.at(args, 1)
feature_name = Enum.at(args, 2)
impression =
Impression.build_from_daemon_response(treatment_data, key, bucketing_key, feature_name)
Telemetry.send_impression(impression)
impression
end
def parse_response(
{:ok, %{"s" => @status_ok, "p" => %{"r" => treatments}}},
%Message{
o: opcode,
a: args
},
_opts
)
when opcode in [@get_treatments_opcode, @get_treatments_with_config_opcode] do
key = Enum.at(args, 0)
bucketing_key = Enum.at(args, 1)
feature_names = Enum.at(args, 2)
impressions =
Enum.zip_reduce(feature_names, treatments, %{}, fn feature_name, treatment, acc ->
impression =
Impression.build_from_daemon_response(treatment, key, bucketing_key, feature_name)
Telemetry.send_impression(impression)
Map.put(acc, feature_name, impression)
end)
impressions
end
def parse_response(
{:ok, %{"s" => @status_ok, "p" => %{"r" => treatments}}},
%Message{
o: opcode,
a: args
},
_opts
)
when opcode in [
@get_treatments_by_flag_set_opcode,
@get_treatments_with_config_by_flag_set_opcode,
@get_treatments_by_flag_sets_opcode,
@get_treatments_with_config_by_flag_sets_opcode
] do
key = Enum.at(args, 0)
bucketing_key = Enum.at(args, 1)
impressions =
treatments
|> Enum.map(fn {feature_name, treatment} ->
impression =
Impression.build_from_daemon_response(treatment, key, bucketing_key, feature_name)
Telemetry.send_impression(impression)
{feature_name, impression}
end)
|> Map.new()
impressions
end
def parse_response(
{:ok, %{"s" => @status_ok, "p" => payload}},
%Message{o: @split_opcode},
_opts
) do
parse_split(payload)
end
def parse_response(
{:ok, %{"s" => @status_ok, "p" => %{"n" => split_names}}},
%Message{
o: @split_names_opcode
},
_opts
) do
split_names
end
def parse_response(
{:ok, %{"s" => @status_ok, "p" => %{"s" => splits}}},
%Message{
o: @splits_opcode
},
_opts
) do
splits =
Enum.reduce(splits, [], fn split, acc ->
[parse_split(split) | acc]
end)
splits
end
def parse_response(
{:ok, %{"s" => @status_ok, "p" => %{"s" => tracked?}}},
%Message{
o: @track_opcode
},
_opts
) do
if tracked? do
true
else
false
end
end
def parse_response(
{:ok, %{"s" => @status_error} = raw_response},
%Message{} = message,
opts
) do
Logger.error("Error response received from Splitd",
request: inspect(message),
response: inspect(raw_response)
)
fallback(message, opts)
end
def parse_response({:ok, raw_response}, %Message{} = message, opts) do
Logger.error("Unable to parse Splitd response",
request: inspect(message),
response: inspect(raw_response)
)
fallback(message, opts)
end
def parse_response({:error, reason}, request, opts) do
Logger.error("Error while communicating with Splitd",
request: inspect(request),
reason: inspect(reason)
)
fallback(request, opts)
end
defp fallback(original_request, opts) do
fallback_response = Fallback.fallback(original_request)
if Keyword.has_key?(opts, :span_context) do
Telemetry.span_event([:rpc, :fallback], opts[:span_context], %{
response: fallback_response
})
end
fallback_response
end
defp parse_split(payload) do
%SplitView{
name: Map.get(payload, "n", nil),
traffic_type: payload["t"],
killed: payload["k"],
treatments: payload["s"],
change_number: payload["c"],
configs: payload["f"],
default_treatment: payload["d"],
sets: payload["e"],
impressions_disabled: payload["i"] || false
}
end
end