Current section
Files
Jump to
Current section
Files
lib/jq.ex
defmodule JQ do
@moduledoc """
Provides capability to run jq queries on elixir structures.
[jq docs](https://stedolan.github.io/jq/)
## Examples
iex> JQ.query(%{key: "value"}, ".key")
{:ok, "value"}
iex> JQ.query!(%{key: "value"}, ".key")
"value"
"""
alias JQ.{MaxByteSizeExceededException, NoResultException, SystemCmdException, UnknownException}
require Logger
@default_options %{max_byte_size: nil}
def query(payload, query, options \\ [])
@spec query(any(), String.t(), list()) :: {:ok, any()} | {:error, :cmd | :unknown}
def query(payload, query, options) do
{:ok, query!(payload, query, options)}
rescue
_ in NoResultException ->
{:ok, nil}
e in [SystemCmdException, UnknownException] ->
Logger.warn(e.message)
{:error, :cmd}
e in MaxByteSizeExceededException ->
Logger.warn(e.message)
{:error, :max_byte_size_exceeded}
end
def query!(payload, query, options \\ [])
@spec query!(any(), String.t(), list()) :: any()
def query!(payload, query, options) do
%{max_byte_size: max_byte_size} = Enum.into(options, @default_options)
json = payload |> Poison.encode!() |> validate_max_byte_size(max_byte_size)
{fd, file_path} = Temp.open!()
IO.write(fd, json)
File.close(fd)
try do
case System.cmd("jq", [query, file_path], stderr_to_stdout: true) do
{_, code} = error when is_integer(code) and code != 0 ->
raise(SystemCmdException, result: error, command: "jq", args: [query, file_path])
{value, code} when is_integer(code) and code == 0 ->
result = Poison.decode!(value)
unless result, do: raise(NoResultException)
result
error ->
raise(UnknownException, error)
end
after
File.rm!(file_path)
end
end
defp validate_max_byte_size(json, max_byte_size)
when is_integer(max_byte_size) and byte_size(json) > max_byte_size do
raise(MaxByteSizeExceededException, size: byte_size(json), max_byte_size: max_byte_size)
end
defp validate_max_byte_size(json, _), do: json
end