Packages
avrora
0.1.1-beta
0.30.2
0.30.1
0.30.0
0.29.2
0.29.1
0.29.0
0.28.0
0.27.0
0.26.0
0.25.0
0.24.2
0.24.1
0.24.0
0.23.0
0.22.0
0.21.1
0.21.0
0.20.0
0.19.0
0.18.1
0.18.0
0.17.0
0.16.0
0.15.0
0.14.1
0.14.0
0.13.0
0.12.0
0.11.0
0.10.0
0.9.1
0.9.0
0.8.1
0.8.0
0.7.1
0.7.0
0.6.0
0.5.1
0.5.0
0.4.0
0.3.1
0.3.0
0.2.2
0.2.1
0.2.0
0.1.1-beta
0.1.0-beta
An Elixir library for working with Avro messages conveniently. It supports local schema files and Confluent® schema registry.
Current section
Files
Jump to
Current section
Files
lib/aurora/storage/registry.ex
defmodule Avrora.Storage.Registry do
@moduledoc """
A small wrapper for [Confluent Schema Registry](https://docs.confluent.io/current/schema-registry/develop/api.html),
with as less as possible functionality. Inspired by [Schemex](https://github.com/bencebalogh/schemex).
"""
require Logger
alias Avrora.{HttpClient, Name, Schema}
@behaviour Avrora.Storage
@content_type "application/vnd.schemaregistry.v1+json"
@doc """
Fetch the latest version of the schema registered under a subject name.
## Examples
iex> {:ok, avro} = Avrora.Storage.Registry.get("io.confluent.Payment")
iex> avro.schema.schema.qualified_names
["io.confluent.Payment"]
"""
def get(key) when is_binary(key) do
with {:ok, schema_name} <- Name.parse(key),
{name, version} <- {schema_name.name, schema_name.version || "latest"},
{:ok, response} <- http_client_get("subjects/#{name}/versions/#{version}"),
{:ok, version} <- Map.fetch(response, "version"),
{:ok, schema} <- Map.fetch(response, "schema"),
{:ok, schema} <- Schema.parse(schema) do
{:ok, %{schema | version: version}}
end
end
@doc """
Fetch a schema by a globally unique ID.
FIXME: Can't use a real docs with iex> because don't know how to make it work
## Examples
...> {:ok, avro} = Avrora.Storage.Registry.get(1)
...> avro.schema.schema.qualified_names
...> ["io.confluent.examples.Payment"]
"""
def get(key) when is_integer(key) do
with {:ok, response} <- http_client_get("schemas/ids/#{key}"),
{:ok, schema} <- Map.fetch(response, "schema"),
{:ok, schema} <- Schema.parse(schema) do
{:ok, %{schema | id: key}}
end
end
@doc """
Register a new version of a schema under the subject name.
## Examples
iex> schema = %{"namespace" => "io.confluent", "type" => "record", "name" => "Payment", "fields" => [%{"name" => "id", "type" => "string"}, %{"name" => "amount", "type" => "double"}]}
iex> {:ok, avro} = Avrora.Storage.Registry.put("io.confluent.examples.Payment", schema)
iex> avro.schema.schema.qualified_names
["io.confluent.Payment"]
"""
def put(key, value) when is_binary(key) and (is_map(value) or is_binary(value)) do
with {:ok, schema_name} <- Name.parse(key),
{:ok, response} <- http_client_post("subjects/#{schema_name.name}/versions", value),
{:ok, id} <- Map.fetch(response, "id"),
{:ok, schema} <- Schema.parse(value) do
unless is_nil(schema_name.version) do
Logger.warn(
"storing schema with version is not allowed, `#{schema_name.name}` used instead"
)
end
{:ok, %{schema | id: id}}
end
end
@doc false
def put(_key, _value), do: {:error, :unsupported}
@doc false
@spec configured?() :: true | false
def configured?, do: !is_nil(Application.get_env(:avrora, :registry_url))
defp http_client_get(path) do
if configured?(),
do: path |> to_url() |> http_client().get() |> handle(),
else: {:error, :unconfigured_registry_url}
end
defp http_client_post(path, payload) do
if configured?() do
path |> to_url() |> http_client().post(payload, content_type: @content_type) |> handle()
else
{:error, :unconfigured_registry_url}
end
end
defp to_url(path), do: "#{Application.get_env(:avrora, :registry_url)}/#{path}"
defp handle({:error, payload} = _response) when is_map(payload) do
reason =
case Map.get(payload, "error_code") do
40401 -> :unknown_subject
40402 -> :unknown_version
40403 -> :unknown_schema
409 -> :conflict
422 -> :unprocessable
_ -> payload
end
{:error, reason}
end
defp handle(response), do: response
defp http_client, do: Application.get_env(:avrora, :http_client, HttpClient)
end