defmodule ConfluentSchema do
@moduledoc """
Provides cache and validation for confluent schemas.
"""
alias ConfluentSchema.{Cache, Server}
alias ExJsonSchema.Validator
@type errors :: [{message :: binary, path :: binary}]
@doc """
Validates the payload against the schema for the given subject.
If you need to validate against multiple schema registries, use the `cache_name` option.
## Examples
iex> ConfluentSchema.validate("a string example", "subject")
{:ok, "a string example"}
iex> ConfluentSchema.validate("a string example", "unknown-subject")
{:error, :not_found}
iex> ConfluentSchema.validate(123, "subject")
{:error, [{"Type mismatch. Expected String but got Integer.", "#"}]}
"""
@spec validate(map, binary, atom()) ::
{:ok, map} | {:error, :not_found} | {:error, errors} | no_return
def validate(payload, subject, cache_name \\ ConfluentSchema.Cache) do
with {:ok, schema} <- Cache.get(subject, cache_name),
:ok <- Validator.validate(schema, payload) do
{:ok, payload}
end
end
@doc """
Returns a `Supervisor.child_spec()` for server to periodically fetch and cache Confluent schemas.
You can get credentials from [Confluent Cloud](https://confluent.cloud): Login > Home > Environments.
Or you can also spin off your own Confluent Schema Registry server.
## Options
* `period` - Period in milliseconds to update schemas (optional integer, default 5 minutes)
* `debug` - Enable debug logs (optional boolean, default false)
* `name` - Name of the GenServer and ETS cache, (optional atom)
Useful when multiple schema registries are needed.
(default `ConfluentSchema.Server` for the GenServer and `ConfluentSchema.Cache` for the ETS table)
## [ConfluentSchemaRegistry](https://hexdocs.pm/confluent_schema_registry/) options
* `base_url` - URL of schema registry (optional, default "http://localhost:8081")
* `username` - username or api key (optional)
* `password` - password or api secret (optional)
* `adapter` - Tesla Adapter (optional, default `Tesla.Adapter.Hackney`)
* `middleware` - List of [Tesla middlewares](https://hexdocs.pm/tesla/readme.html#middleware) (optional)
* `local` - Whether to use local schemas from `priv/confluent_schema/*.json` (optional, default false
(when set all above options are ignored)
* `app_name` - Name of the application where local schemas are (required only if local is true)
## Example
opts = [
name: :my_custom_registry,
period: :timer.minutes(5),
debug: false,
base_url: "http://localhost:8081",
username: "key",
password: "api secret",
adapter: {Tesla.Adapter.Hackney, hackney_opts},
middleware: []
]
children = [{ConfluentSchema, opts}]
Supervisor.start_link(children, strategy: :one_for_one)
"""
@spec child_spec(Keyword.t()) :: Supervisor.child_spec()
def child_spec(opts) do
%{
id: Server,
start: {Server, :start_link, [opts]}
}
end
end