defmodule Polymarket.JsonUtil do @moduledoc """ Helpers for massaging raw JSON (as decoded from an external API) into the shapes our schemas expect. """ @doc """ Recursively rewrites a map's `camelCase` keys (atom or string) into `snake_case` string keys, descending into nested maps and lists. Bridges the Gamma API's `camelCase` payloads to our `snake_case` schema fields. iex> Polymarket.JsonUtil.snake_case_keys(%{conditionId: "0x1", feeSchedule: %{rebateRate: 0.2}}) %{"condition_id" => "0x1", "fee_schedule" => %{"rebate_rate" => 0.2}} """ @spec snake_case_keys(map()) :: map() def snake_case_keys(attrs) when is_map(attrs) do Recase.Enumerable.stringify_keys(attrs, &Recase.to_snake/1) end @doc """ Fetches `key` from `map`, accepting either the atom key or its string form. The HTTP layer decodes JSON to atom keys, but a body we re-decoded locally has string keys — so try the atom first, then fall back to the string. """ @spec lenient_fetch(map(), atom()) :: {:ok, term()} | :error def lenient_fetch(map, key) when is_atom(key) do with :error <- Map.fetch(map, key) do Map.fetch(map, Atom.to_string(key)) end end end