defmodule DiogramosWeb.Api.V1.CanvasController do
@moduledoc """
Canvas metadata CRUD and exports.
There is deliberately **no endpoint here that writes `document`**. Every
document mutation goes through `DiogramosWeb.CanvasChannel` into
`Diogramos.Diagrams.Authority`, which is the only thing allowed to
sequence ops and bump `version`. A second HTTP write path into the same
GenServer would have no ordering story.
"""
use DiogramosWeb, :controller
alias Diogramos.Diagrams
alias Diogramos.Diagrams.{Authority, MermaidExport, Permissions}
action_fallback DiogramosWeb.Api.FallbackController
def index(conn, params) do
scope = conn.assigns.current_scope
canvases =
case Map.fetch(params, "folder_id") do
{:ok, ""} -> Diagrams.list_canvases_in_folder(scope, nil)
{:ok, "none"} -> Diagrams.list_canvases_in_folder(scope, nil)
{:ok, id} -> Diagrams.list_canvases_in_folder(scope, String.to_integer(id))
:error -> Diagrams.list_canvases(scope)
end
render(conn, :index, canvases: canvases, roles: roles_for(scope, canvases))
end
def show(conn, %{"slug" => slug}) do
scope = conn.assigns.current_scope
with {:ok, canvas} <- fetch_canvas(scope, slug) do
# Read through the authority, not the row: a canvas being actively
# edited holds unpersisted ops in memory (it only snapshots every
# 25), so the DB copy can be stale by up to 25 ops.
{document, version} =
case Authority.snapshot(canvas.id) do
{:ok, %{document: doc, version: version}} -> {doc, version}
{:error, _} -> {canvas.document, canvas.version}
end
canvas = %{canvas | document: document, version: version}
render(conn, :show, canvas: canvas, role: Permissions.effective_role(scope, canvas))
end
end
def create(conn, params) do
scope = conn.assigns.current_scope
with {:ok, canvas} <- Diagrams.create_canvas(scope, canvas_params(params)) do
conn
|> put_status(:created)
|> render(:show, canvas: canvas, role: Permissions.effective_role(scope, canvas))
end
end
def update(conn, %{"slug" => slug} = params) do
scope = conn.assigns.current_scope
with {:ok, canvas} <- fetch_canvas(scope, slug),
{:ok, canvas} <- Diagrams.update_canvas_metadata(scope, canvas, canvas_params(params)) do
render(conn, :show, canvas: canvas, role: Permissions.effective_role(scope, canvas))
end
end
def delete(conn, %{"slug" => slug}) do
scope = conn.assigns.current_scope
with {:ok, canvas} <- fetch_canvas(scope, slug),
{:ok, _canvas} <- Diagrams.delete_canvas(scope, canvas) do
send_resp(conn, :no_content, "")
end
end
def export(conn, %{"slug" => slug}) do
scope = conn.assigns.current_scope
with {:ok, canvas} <- fetch_canvas(scope, slug),
{:ok, payload} <- Diagrams.export_canvas(scope, canvas) do
json(conn, payload)
end
end
def export_mermaid(conn, %{"slug" => slug}) do
scope = conn.assigns.current_scope
with {:ok, canvas} <- fetch_canvas(scope, slug) do
document =
case Authority.snapshot(canvas.id) do
{:ok, %{document: doc}} -> doc
{:error, _} -> canvas.document
end
conn
|> put_resp_content_type("text/plain")
|> send_resp(200, MermaidExport.to_mermaid(document))
end
end
## Helpers
defp fetch_canvas(scope, slug) do
case Diagrams.get_canvas_by_slug(scope, slug) do
nil -> {:error, :not_found}
canvas -> {:ok, canvas}
end
end
defp canvas_params(params) do
Map.take(params, ["slug", "title", "theme", "folder_id", "embed_show_cursors"])
end
defp roles_for(scope, canvases) do
Map.new(canvases, &{&1.id, Permissions.effective_role(scope, &1)})
end
end
neiam
/diogramos
diogramos
public · Issues · Pulls · Labels · Forks · Compare · Actions success · Badges · Packages
⭐
Log in to mark this repository.
3.7 KiB · text
History
5602f02