• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

tgrk / ex_oura / 72c22bfba344cfcc7de33da1ad6ddb7e3f8652cd-PR-1

16 Dec 2024 03:35PM UTC coverage: 73.832%. First build
72c22bfba344cfcc7de33da1ad6ddb7e3f8652cd-PR-1

Pull #1

github

tgrk
feat: improve type handling using a decoder
Pull Request #1: An initial version

316 of 428 new or added lines in 103 files covered. (73.83%)

316 of 428 relevant lines covered (73.83%)

24268.22 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

84.85
/lib/type_decoder.ex
1
defmodule ExOura.TypeDecoder do
2
  @moduledoc """
3
  Decode a response body based on types from the operation
4
  """
5

6
  @spec decode_response(status :: pos_integer(), body :: term(), operation :: map()) ::
7
          {:ok, term()} | {:error, :unable_to_decode}
8
  def decode_response(200 = _status, [], _operation), do: {:ok, []}
1✔
9

10
  def decode_response(status, body, operation) do
11
    case get_type(operation, status) do
72✔
12
      {:ok, response_type} ->
54✔
13
        {:ok, decode(body, response_type)}
14

15
      {:error, :unable_to_decode} = error ->
16
        error
18✔
17
    end
18
  end
19

20
  defp get_type(%{response: response} = _operation, status) do
21
    case response |> Enum.into(%{}) |> Map.get(status, nil) do
72✔
22
      [{_response_type, :t}] = type ->
1✔
23
        {:ok, type}
24

25
      {_response_type, :t} = type ->
53✔
26
        {:ok, type}
27

28
      _unexpected ->
18✔
29
        {:error, :unable_to_decode}
30
    end
31
  end
32

33
  defp decode(nil, _response_type), do: nil
547✔
34
  defp decode(value, {:string, :date}), do: Date.from_iso8601!(value)
8,613✔
35
  defp decode(value, {:union, types}), do: decode(value, union(value, types))
3,276,013✔
36

37
  defp decode(value, [type]), do: Enum.map(value, &decode(&1, type))
2,405✔
38

39
  defp decode(%{} = value, {module, type}) do
40
    base = prepare_base(module)
20,869✔
41
    fields = module.__fields__(type)
20,869✔
42

43
    for {field_name, field_type} <- fields, reduce: base do
20,869✔
44
      decoded_value ->
45
        case Map.get(value, field_name) do
155,273✔
46
          nil ->
47
            decoded_value
1,031✔
48

49
          field_value ->
50
            Map.put(decoded_value, field_name, decode(field_value, field_type))
154,242✔
51
        end
52
    end
53
  end
54

55
  defp decode(value, {type, :t}) do
56
    with true <- type |> to_string() |> String.ends_with?("Timestamp"),
9,539✔
57
         {:ok, dt, _} <- DateTime.from_iso8601(value) do
8,829✔
58
      dt
8,829✔
59
    else
60
      _error ->
61
        value
710✔
62
    end
63
  end
64

65
  defp decode(value, _type), do: value
3,336,777✔
66

67
  defp prepare_base(module) do
68
    Code.ensure_loaded(module)
20,869✔
69

70
    if function_exported?(module, :__struct__, 0) do
20,869✔
71
      struct(module)
20,869✔
72
    else
NEW
73
      %{}
×
74
    end
75
  end
76

77
  defp union(_value, [type, :null]), do: type
3,276,009✔
NEW
78
  defp union(value, [:number, {:string, :generic}]) when is_number(value), do: :number
×
NEW
79
  defp union(_value, [:number, {:string, :generic}]), do: :string
×
NEW
80
  defp union(value, [:integer, {:string, :generic}]) when is_number(value), do: :integer
×
81
  defp union(_value, [:integer, {:string, :generic}]), do: :string
4✔
82

83
  defp union(_value, types) do
NEW
84
    raise "TypedDecoder: Unable to decode union type #{inspect(types)}"
×
85
  end
86
end
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc