diff --git a/lib/atlas/feedbacks.ex b/lib/atlas/feedbacks.ex new file mode 100644 index 0000000..50baab7 --- /dev/null +++ b/lib/atlas/feedbacks.ex @@ -0,0 +1,106 @@ +defmodule Atlas.Feedbacks do + @moduledoc """ + The Feedbacks context. + """ + + use Atlas.Context + alias Atlas.Repo + + alias Atlas.Feedbacks.Feedback + + @doc """ + Returns the list of feedbacks. + + ## Examples + + iex> list_feedbacks() + [%Feedback{}, ...] + + """ + def list_feedbacks(opts \\ []) do + Feedback + |> apply_filters(opts) + |> Repo.all() + end + + @doc """ + Gets a single feedback. + + Raises `Ecto.NoResultsError` if the Feedback does not exist. + + ## Examples + + iex> get_feedback!(123) + %Feedback{} + + iex> get_feedback!(456) + ** (Ecto.NoResultsError) + + """ + def get_feedback!(id), do: Repo.get(Feedback, id) + + @doc """ + Creates a feedback. + + ## Examples + + iex> create_feedback(%{field: value}) + {:ok, %Feedback{}} + + iex> create_feedback(%{field: bad_value}) + {:error, %Ecto.Changeset{}} + + """ + def create_feedback(attrs \\ %{}) do + %Feedback{} + |> Feedback.changeset(attrs) + |> Repo.insert() + end + + @doc """ + Updates a feedback. + + ## Examples + + iex> update_feedback(feedback, %{field: new_value}) + {:ok, %Feedback{}} + + iex> update_feedback(feedback, %{field: bad_value}) + {:error, %Ecto.Changeset{}} + + """ + def update_feedback(%Feedback{} = feedback, attrs) do + feedback + |> Feedback.changeset(attrs) + |> Repo.update() + end + + @doc """ + Deletes a feedback. + + ## Examples + + iex> delete_feedback(feedback) + {:ok, %Feedback{}} + + iex> delete_feedback(feedback) + {:error, %Ecto.Changeset{}} + + """ + def delete_feedback(%Feedback{} = feedback) do + Repo.delete(feedback) + end + + @doc """ + Returns an `%Ecto.Changeset{}` for tracking feedback changes. + + ## Examples + + iex> change_feedback(feedback) + %Ecto.Changeset{data: %Feedback{}} + + """ + def change_feedback(%Feedback{} = feedback, attrs \\ %{}) do + Feedback.changeset(feedback, attrs) + end +end diff --git a/lib/atlas/feedbacks/feedback.ex b/lib/atlas/feedbacks/feedback.ex new file mode 100644 index 0000000..a364734 --- /dev/null +++ b/lib/atlas/feedbacks/feedback.ex @@ -0,0 +1,26 @@ +defmodule Atlas.Feedbacks.Feedback do + @moduledoc """ + The Feedback schema. + """ + + use Ecto.Schema + import Ecto.Changeset + + @required_fields ~w(subject message user_id)a + @primary_key {:id, :binary_id, autogenerate: true} + @foreign_key_type :binary_id + schema "feedbacks" do + field :message, :string + field :subject, :string + field :user_id, :binary_id + + timestamps(type: :utc_datetime) + end + + @doc false + def changeset(feedback, attrs) do + feedback + |> cast(attrs, @required_fields) + |> validate_required(@required_fields) + end +end diff --git a/lib/atlas_web/controllers/feedbacks/feedback_json.ex b/lib/atlas_web/controllers/feedbacks/feedback_json.ex new file mode 100644 index 0000000..e26060f --- /dev/null +++ b/lib/atlas_web/controllers/feedbacks/feedback_json.ex @@ -0,0 +1,27 @@ +defmodule AtlasWeb.FeedbacksJSON do + alias Atlas.Feedbacks.Feedback + + @doc """ + Renders a list of feedbacks. + """ + def index(%{feedbacks: feedbacks}) do + %{data: for(feedback <- feedbacks, do: data(feedback))} + end + + @doc """ + Renders a single feedback. + """ + def show(%{feedback: feedback}) do + %{data: data(feedback)} + end + + defp data(%Feedback{} = feedback) do + %{ + id: feedback.id, + subject: feedback.subject, + message: feedback.message, + user_id: feedback.user_id, + inserted_at: feedback.inserted_at + } + end +end diff --git a/lib/atlas_web/controllers/feedbacks/feedbacks_controller.ex b/lib/atlas_web/controllers/feedbacks/feedbacks_controller.ex new file mode 100644 index 0000000..391a3be --- /dev/null +++ b/lib/atlas_web/controllers/feedbacks/feedbacks_controller.ex @@ -0,0 +1,64 @@ +defmodule AtlasWeb.FeedbacksController do + use AtlasWeb, :controller + + alias Atlas.Feedbacks + alias Atlas.Feedbacks.Feedback + + action_fallback AtlasWeb.FallbackController + + def index(conn, _attrs) do + {user, _session} = Guardian.Plug.current_resource(conn) + + feedbacks = + if user_has_elevated_privileges?(user) do + Feedbacks.list_feedbacks() + else + Feedbacks.list_feedbacks(where: [user_id: user.id]) + end + + render(conn, :index, feedbacks: feedbacks) + end + + def create(conn, attrs) do + {user, _session} = Guardian.Plug.current_resource(conn) + attrs = Map.put(attrs, "user_id", user.id) + + case Feedbacks.create_feedback(attrs) do + {:ok, feedback} -> + conn + |> put_status(:created) + |> render(:show, feedback: feedback) + + {:error, _changeset} -> + conn + |> put_status(:unprocessable_entity) + |> json(%{errors: "Invalid fields"}) + end + end + + def delete(conn, %{"id" => id}) do + {user, _session} = Guardian.Plug.current_resource(conn) + + if user_has_elevated_privileges?(user) do + case Feedbacks.get_feedback!(id) do + nil -> + conn + |> put_status(:not_found) + |> json(%{error: "Feedback not found"}) + + feedback -> + with {:ok, %Feedback{}} <- Feedbacks.delete_feedback(feedback) do + send_resp(conn, :no_content, "") + end + end + else + conn + |> put_status(:unauthorized) + |> json(%{error: "Unauthorized"}) + end + end + + defp user_has_elevated_privileges?(user) do + (user && user.type == :admin) || user.type == :professor + end +end diff --git a/lib/atlas_web/router.ex b/lib/atlas_web/router.ex index a5309ef..8a8348c 100644 --- a/lib/atlas_web/router.ex +++ b/lib/atlas_web/router.ex @@ -89,6 +89,8 @@ defmodule AtlasWeb.Router do resources "/", ShiftExchangeRequestController, only: [:index, :create, :show, :delete] end + resources "/feedbacks", FeedbacksController, except: [:new, :edit, :show] + pipe_through :is_at_least_professor get "/students", University.StudentsController, :index diff --git a/priv/repo/migrations/20250928211951_create_feedbacks.exs b/priv/repo/migrations/20250928211951_create_feedbacks.exs new file mode 100644 index 0000000..439cf93 --- /dev/null +++ b/priv/repo/migrations/20250928211951_create_feedbacks.exs @@ -0,0 +1,16 @@ +defmodule Atlas.Repo.Migrations.CreateFeedbacks do + use Ecto.Migration + + def change do + create table(:feedbacks, primary_key: false) do + add :id, :binary_id, primary_key: true + add :subject, :string + add :message, :string + add :user_id, references(:users, on_delete: :nothing, type: :binary_id) + + timestamps(type: :utc_datetime) + end + + create index(:feedbacks, [:user_id]) + end +end diff --git a/test/atlas/degrees_test.exs b/test/atlas/degrees_test.exs index 2fd2d47..0484651 100644 --- a/test/atlas/degrees_test.exs +++ b/test/atlas/degrees_test.exs @@ -13,7 +13,8 @@ defmodule Atlas.DegreesTest do test "list_degrees/0 returns all degrees" do degree = degree_fixture() - assert Degrees.list_degrees() == [degree] + degrees = Degrees.list_degrees() + assert degree in degrees end test "get_degree!/1 returns the degree with given id" do