Skip to content

Commit

Permalink
fix: Update Configuration to include realtime config settings (#92)
Browse files Browse the repository at this point in the history
  • Loading branch information
w3b6x9 authored Dec 21, 2020
1 parent 491cb79 commit a057264
Show file tree
Hide file tree
Showing 4 changed files with 184 additions and 68 deletions.
128 changes: 84 additions & 44 deletions server/lib/realtime/configuration.ex
Original file line number Diff line number Diff line change
@@ -1,79 +1,119 @@
defmodule Realtime.Configuration do
defmodule(ConfigurationFileError,
do: defexception(message: "configuration file is missing required attribute(s)")
)

defmodule(WebhookEndpoint, do: defstruct([:endpoint]))
defmodule(Webhook, do: defstruct([:event, :relation, :config]))
defmodule(Realtime, do: defstruct([:events, :relation]))

defmodule(Configuration, do: defstruct([:webhooks, :realtime]))
defmodule(Configuration, do: defstruct(webhooks: [], realtime: []))

@events ["INSERT", "UPDATE", "DELETE"]

@doc """
Load Configuration from a json file.
Load and convert configuration settings from a json file.
"""
def from_json_file(nil) do
def from_json_file(filename) when is_binary(filename) and filename != "" do
{:ok,
%Configuration{
webhooks: [],
realtime: [
filename
|> File.read!()
|> Jason.decode!()
|> convert_raw_config!()}
end

def from_json_file(_) do
{:ok,
Map.put(
%Configuration{},
:realtime,
[
%Realtime{
relation: "*",
events: ["INSERT", "UPDATE", "DELETE"]
events: @events
},
%Realtime{
relation: "*:*",
events: ["INSERT", "UPDATE", "DELETE"]
events: @events
},
%Realtime{
relation: "*:*:*",
events: ["INSERT", "UPDATE", "DELETE"]
events: @events
}
]
}}
)}
end

def from_json_file(filename) do
with {:ok, body} <- File.read(filename), do: from_json(body)
defp convert_raw_config!(raw_config) do
default_config = %Configuration{}

default_config
|> Map.keys()
|> Enum.reduce(default_config, fn connector_type, acc_config ->
convert_connector_config!(acc_config, raw_config, connector_type)
end)
end

@doc """
Load Configuration from a json string.
"""
def from_json(body) do
with {:ok, raw_config} <- Jason.decode(body), do: to_configuration(raw_config)
defp convert_connector_config!(%Configuration{} = config, %{"webhooks" => webhooks}, :webhooks)
when is_list(webhooks) do
webhooks =
Enum.map(webhooks, fn webhook ->
case Kernel.get_in(webhook, ["config", "endpoint"]) do
endpoint when is_binary(endpoint) and endpoint != "" ->
config_endpoint = %WebhookEndpoint{endpoint: endpoint}
# default to all events
event = Map.get(webhook, "event", "*")
# default to all relations
relation = Map.get(webhook, "relation", "*")

%Webhook{event: event, relation: relation, config: config_endpoint}

_ ->
raise ConfigurationFileError
end
end)

%Configuration{config | webhooks: webhooks}
end

defp to_configuration(config) do
with {:ok, raw_webhooks} <- Map.fetch(config, "webhooks"),
{:ok, webhooks} <- to_webhooks_configuration(raw_webhooks)
do
{:ok, %Configuration{webhooks: webhooks}}
end
defp convert_connector_config!(%Configuration{}, %{"webhooks" => _}, :webhooks) do
raise ConfigurationFileError
end

defp to_webhooks_configuration(config) do
to_webhooks_configuration(config, [])
defp convert_connector_config!(%Configuration{} = config, _, :webhooks) do
config
end
defp to_webhooks_configuration([], acc), do: {:ok, Enum.reverse(acc)}
defp to_webhooks_configuration([config | rest], acc) do
case to_webhook_configuration(config) do
{:ok, config} -> to_webhooks_configuration(rest, [config | acc])
_ -> :error
end

defp convert_connector_config!(
%Configuration{} = config,
%{"realtime" => subscribers},
:realtime
)
when is_list(subscribers) do
subscribers =
Enum.map(subscribers, fn subscriber ->
with {:ok, relation} <- Map.fetch(subscriber, "relation"),
{:ok, events} <- Map.fetch(subscriber, "events") do
%Realtime{relation: relation, events: events}
else
_ -> raise ConfigurationFileError
end
end)

%Configuration{config | realtime: subscribers}
end

defp convert_connector_config!(%Configuration{}, %{"realtime" => _}, :realtime) do
raise ConfigurationFileError
end

defp to_webhook_configuration(config) do
with {:ok, raw_endpoint} <- Map.fetch(config, "config"),
{:ok, endpoint} <- to_webhook_endpoint_configuration(raw_endpoint)
do
event = Map.get(config, "events", "*") # default to all events
relation = Map.get(config, "relation", "*") # default to all relations
{:ok, %Webhook{event: event, relation: relation, config: endpoint}}
end
defp convert_connector_config!(%Configuration{} = config, _, :realtime) do
config
end

defp to_webhook_endpoint_configuration(config) do
with {:ok, endpoint} <- Map.fetch(config, "endpoint")
do
{:ok, %WebhookEndpoint{endpoint: endpoint}}
end
defp convert_connector_config!(%Configuration{} = config, _, :__struct__) do
config
end
end
69 changes: 63 additions & 6 deletions server/test/realtime/configuration_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,72 @@ defmodule Realtime.ConfigurationTest do
use ExUnit.Case

alias Realtime.Configuration
alias Realtime.Configuration.{Realtime, Webhook, WebhookEndpoint}

test "parse a configuration from a json file" do
test "Realtime.Configuration.from_json_file/1 when filename not given" do
{:ok, config} = Configuration.from_json_file(nil)

assert config == %Configuration.Configuration{
webhooks: [],
realtime: [
%Realtime{
relation: "*",
events: ["INSERT", "UPDATE", "DELETE"]
},
%Realtime{
relation: "*:*",
events: ["INSERT", "UPDATE", "DELETE"]
},
%Realtime{
relation: "*:*:*",
events: ["INSERT", "UPDATE", "DELETE"]
}
]
}
end

test "Realtime.Configuration.from_json_file/1 when file contains an empty JSON object" do
filename = Path.expand("../support/example_empty_config.json", __DIR__)

{:ok, config} = Configuration.from_json_file(filename)

assert config == %Configuration.Configuration{webhooks: [], realtime: []}
end

test "Realtime.Configuration.from_json_file/1 when file contains configuration attributes" do
filename = Path.expand("../support/example_config.json", __DIR__)

{:ok, config} = Configuration.from_json_file(filename)
assert Enum.count(config.webhooks) == 3

webhook = List.first(config.webhooks)
assert webhook.event == "*"
assert webhook.relation == "*"
assert String.starts_with?(webhook.config.endpoint, "https://")
assert config == %Configuration.Configuration{
realtime: [
%Realtime{events: ["INSERT", "UPDATE"], relation: "public"},
%Realtime{events: ["INSERT"], relation: "public:users"},
%Realtime{events: ["DELETE"], relation: "public:users.id=eq.1"}
],
webhooks: [
%Webhook{
config: %WebhookEndpoint{
endpoint: "https://webhook.site/44e4457a-77ae-4758-8d52-17efdf484853"
},
event: "*",
relation: "*"
},
%Webhook{
config: %WebhookEndpoint{
endpoint: "https://webhook.site/44e4457a-77ae-4758-8d52-17efdf484853"
},
event: "INSERT",
relation: "public"
},
%Webhook{
config: %WebhookEndpoint{
endpoint: "https://webhook.site/44e4457a-77ae-4758-8d52-17efdf484853"
},
event: "UPDATE",
relation: "public:users.id=eq.2"
}
]
}
end
end
54 changes: 36 additions & 18 deletions server/test/support/example_config.json
Original file line number Diff line number Diff line change
@@ -1,20 +1,38 @@
{
"webhooks": [{
"event": "*",
"config": {
"endpoint": "https://webhook.site/44e4457a-77ae-4758-8d52-17efdf484853"
}
}, {
"event": "INSERT",
"relation": "public",
"config": {
"endpoint": "https://webhook.site/44e4457a-77ae-4758-8d52-17efdf484853"
}
}, {
"event": "UPDATE",
"relation": "public:users.id=eq.2",
"config": {
"endpoint": "https://webhook.site/44e4457a-77ae-4758-8d52-17efdf484853"
}
}]
"webhooks": [
{
"event": "*",
"config": {
"endpoint": "https://webhook.site/44e4457a-77ae-4758-8d52-17efdf484853"
}
},
{
"event": "INSERT",
"relation": "public",
"config": {
"endpoint": "https://webhook.site/44e4457a-77ae-4758-8d52-17efdf484853"
}
},
{
"event": "UPDATE",
"relation": "public:users.id=eq.2",
"config": {
"endpoint": "https://webhook.site/44e4457a-77ae-4758-8d52-17efdf484853"
}
}
],
"realtime": [
{
"events": ["INSERT", "UPDATE"],
"relation": "public"
},
{
"events": ["INSERT"],
"relation": "public:users"
},
{
"events": ["DELETE"],
"relation": "public:users.id=eq.1"
}
]
}
1 change: 1 addition & 0 deletions server/test/support/example_empty_config.json
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{}

0 comments on commit a057264

Please sign in to comment.