Current section

Files

Jump to
eddb lib facade.ex
Raw

lib/facade.ex

defmodule Eddb.Facade do
@moduledoc """
A facade providing easy-to-use commands for managing users, topics, and entries.
"""
require Logger
alias User.User
alias Database.Worker
alias Database.Database
# User Management
def create_user(username, password, permissions \\ []) do
User.create_user(username, password, permissions)
end
def delete_user(username) do
User.delete_user(username)
end
def add_topic_to_user(username, topic) do
User.add_topic(username, topic)
end
def remove_topic_from_user(username, topic) do
User.remove_topic(username, topic)
end
def list_users do
User.sync()
Process.whereis(:user_server) |> GenServer.call({:get_state})
end
# Database/Topic Management
def list_topics do
loaded_topics = Database.list_topics()
nodes = [node() | Node.list()]
disk_topics =
nodes
|> Enum.flat_map(fn node ->
:rpc.call(node, Path, :wildcard, ["topic_*.json"])
|> Enum.map(fn path ->
path
|> String.replace("topic_", "")
|> String.replace(".json", "")
end)
end)
(loaded_topics ++ disk_topics)
|> Enum.uniq()
end
def create_topic(topic_name) do
# In this system, topics are created on first put.
# We can "create" one by putting a dummy key or just acknowledging it's dynamic.
# But for the facade, we'll just say it's ready.
IO.puts("Topic #{topic_name} will be created upon first entry.")
:ok
end
def delete_topic(topic_name) do
Database.delete_topic(topic_name)
end
# Entry Management
def put(topic, key, value) do
worker = Database.get_worker(key)
GenServer.call(worker, {:put, topic, key, value})
end
def get(topic, key) do
worker = Database.get_worker(key)
GenServer.call(worker, {:get, topic, key})
end
def delete(topic, key) do
worker = Database.get_worker(key)
GenServer.call(worker, {:delete, topic, key})
end
# Sync Management
def sync do
# 1. Sync users
sync_users()
# 2. Sync all topics
sync_topics()
end
def sync_topic(topic_name) do
nodes = [node() | Node.list()]
nodes
|> Enum.each(fn node ->
case Database.get_workers_with_topic(node, topic_name) do
[] ->
# No worker has this topic loaded
{:ok, pid} = :rpc.call(node, GenServer, :start_link, [Worker, []])
GenServer.call(pid, {:sync, topic_name})
GenServer.stop(pid)
workers ->
# One or more workers have this topic, sync all of them
Enum.each(workers, fn pid ->
:rpc.call(node, GenServer, :call, [pid, {:sync, topic_name}])
end)
end
end)
:ok
end
def sync_topics do
list_topics()
|> Enum.each(&sync_topic/1)
:ok
end
def sync_users do
User.sync()
end
# Helpers
def query_topic(topic_name) do
workers = Database.get_workers_with_topic(node(), topic_name)
initial_state =
case workers do
[worker_pid | _] ->
state = GenServer.call(worker_pid, {:get_state})
topic = Enum.find(state, &(&1.topic == topic_name))
[topic]
[] ->
[]
end
{:ok, pid} = GenServer.start_link(Worker, initial_state)
synced_topic = GenServer.call(pid, {:sync, topic_name})
GenServer.stop(pid)
synced_topic
end
end