Packages

Elixir KSQL client library.

Current section

Files

Jump to
ksql lib ksql.ex
Raw

lib/ksql.ex

defmodule KSQL do
@moduledoc """
Documentation for KSQL.
"""
def stream!(stream) do
with {:ok, data} <- stream(stream) do
data
else
{:error, error} -> raise "StreamError: #{error}"
end
end
def stream(stream) do
with {:ok, events} <- query("SELECT * FROM #{stream.__resource__(:source)};") do
{:ok,
Stream.map(events, fn %{"row" => %{"columns" => data}} ->
struct!(stream, :fields |> stream.__resource__() |> Enum.zip(data))
end)}
end
end
def query(query),
do:
if(String.starts_with?(query, "SELECT "), do: run_query(query), else: run_statement(query))
### Helpers ###
# Move to different module
defp run_query(query) do
with {:ok, ref} <-
HTTPX.request(
:post,
url("query"),
body:
Jason.encode!(%{
ksql: query,
streamsProperties: %{"ksql.streams.auto.offset.reset": "earliest"}
}),
headers: [
{"Accept", "application/json"},
{"Content-Type", "application/json"}
],
format: :json_atoms,
settings: [async: true]
) do
setup_stream(ref)
end
end
defp run_statement(query) do
with {:ok, %{body: body}} <-
HTTPX.request(
:post,
url("ksql"),
body:
Jason.encode!(%{
ksql: query,
streamsProperties: %{}
}),
headers: [
{"Content-Type", "application/vnd.ksql.v1+json; charset=utf-8"}
],
format: :json_atoms
) do
result = Enum.map(body, &parse_response/1)
if Enum.count(result) == 1 do
{:ok, List.first(result)}
else
{:ok, result}
end
end
end
defp url(path), do: Application.get_env(:ksql, :url) <> path
defp setup_stream(ref) do
{status, data} =
receive do
{:hackney_response, ^ref, {:status, status, data}} -> {status, data}
end
headers =
receive do
{:hackney_response, ^ref, {:headers, headers}} -> headers
end
if status == 200 do
{:ok, create_stream(ref)}
else
{:error, %{status: status, data: data, headers: headers}}
end
end
defp create_stream(ref) do
Stream.resource(
fn -> ref end,
fn ref ->
receive do
{:hackney_response, ^ref, :done} -> {:halt, ref}
{:hackney_response, ^ref, "\n"} -> {[], ref}
{:hackney_response, ^ref, data} -> {[Jason.decode!(data)], ref}
end
end,
fn _ref -> :ok end
)
end
defp parse_response(%{"@type": "streams", streams: streams}) do
Enum.map(streams, &struct!(KSQL.Stream, &1))
end
defp parse_response(%{"@type": "tables", tables: tables}) do
Enum.map(tables, &struct!(KSQL.Table, &1))
end
defp parse_response(a), do: a
end