frenzy/lib/frenzy_web/controllers/pipeline_controller.ex

160 lines
4.3 KiB
Elixir

defmodule FrenzyWeb.PipelineController do
use FrenzyWeb, :controller
alias Frenzy.{Repo, Pipeline}
alias FrenzyWeb.Router.Helpers, as: Routes
alias FrenzyWeb.Endpoint
import Ecto.Query
plug :user_owns_pipeline
defp user_owns_pipeline(%Plug.Conn{path_params: %{"id" => pipeline_id}} = conn, _opts) do
user = conn.assigns[:user]
pipeline = Repo.get(Pipeline, pipeline_id)
if pipeline.user_id == user.id do
conn
|> assign(:pipeline, pipeline)
else
conn
|> put_flash(:error, "You do not have permission to access that resource.")
|> redirect(to: Routes.group_path(Endpoint, :index))
|> halt()
end
end
defp user_owns_pipeline(conn, _opts), do: conn
def index(conn, _params) do
user = conn.assigns[:user]
pipelines = Repo.all(from p in Pipeline, where: p.user_id == ^user.id, preload: [:feeds])
render(conn, "index.html", %{pipelines: pipelines})
end
def new(conn, _params) do
changeset =
Pipeline.changeset(%Pipeline{}, %{
name: "",
stages: "[\n]"
})
render(conn, "new.html", %{changeset: changeset})
end
def create(conn, %{"pipeline" => %{"name" => name, "stages" => stages_json}}) do
user = conn.assigns[:user]
with {:ok, stages} <- Jason.decode(stages_json),
{:ok, stages} <- validate_pipeline(stages) do
changeset = Pipeline.changeset(%Pipeline{}, %{user_id: user.id, name: name, stages: stages})
{:ok, _pipeline} = Repo.insert(changeset)
conn
|> put_flash(:info, "Pipeline created")
|> redirect(to: Routes.pipeline_path(Endpoint, :index))
else
{:error, reason} ->
error_changeset = Pipeline.changeset(%Pipeline{}, %{name: name, stages: stages_json})
conn
|> put_flash(:error, "Unable to create pipeline: #{reason}")
|> render("new.html", %{changeset: error_changeset})
end
end
def show(conn, _params) do
pipeline = conn.assigns[:pipeline]
render(conn, "show.html", %{pipeline: pipeline})
end
def delete(conn, _params) do
conn.assigns[:pipeline]
|> Repo.delete()
redirect(conn, to: Routes.pipeline_path(Endpoint, :index))
end
def edit(conn, _params) do
pipeline = conn.assigns[:pipeline]
{:ok, stages_json} = Jason.encode(pipeline.stages, pretty: true)
render(conn, "edit.html", %{
pipeline: pipeline,
name: pipeline.name,
stages_json: stages_json
})
end
def update(conn, %{"pipeline" => %{"name" => name, "stages" => stages_json}}) do
pipeline = conn.assigns[:pipeline]
with {:ok, stages} <- Jason.decode(stages_json),
{:ok, stages} <- validate_pipeline(stages) do
changeset = Pipeline.changeset(pipeline, %{name: name, stages: stages})
{:ok, _pipeline} = Repo.update(changeset)
conn
|> put_flash(:info, "Pipeline edited")
|> redirect(to: Routes.pipeline_path(Endpoint, :show, pipeline.id))
else
{:error, reason} ->
conn
|> put_flash(:error, "Unable to edit pipeline: #{reason}")
|> render("edit.html", %{
pipeline: pipeline,
name: name,
stages_json: stages_json
})
end
end
defp validate_pipeline(stages) do
stages
|> Enum.with_index()
|> Enum.reduce_while({:ok, []}, fn {stage, index}, {:ok, new_stages} ->
case validate_stage(stage) do
{:ok, stage} ->
{:cont, {:ok, new_stages ++ [stage]}}
{:error, reason} ->
{:halt, {:error, "invalid stage at index #{index}: #{reason}"}}
end
end)
end
defp validate_stage(%{"module_name" => module_name, "options" => options}) do
with true <- module_exists(module_name),
{:ok, options} <-
apply(String.to_existing_atom("Elixir." <> module_name), :validate_opts, [
options
]) do
{:ok, %{"module_name" => module_name, "options" => options}}
else
false ->
{:error, "module #{module_name} does not exist"}
{:error, reason} ->
{:error, "invalid options for #{module_name}: #{reason}"}
end
end
defp validate_stage(_),
do: {:error, "pipeline stage must be a map with module_name and options"}
defp module_exists(module_name) do
try do
String.to_existing_atom("Elixir." <> module_name)
true
rescue
ArgumentError ->
false
end
end
end