-
Notifications
You must be signed in to change notification settings - Fork 86
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #1278 from code-corps/add-messages
Add messages
- Loading branch information
Showing
17 changed files
with
829 additions
and
4 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
defmodule CodeCorps.Messages do | ||
@moduledoc ~S""" | ||
Main context for work with the Messaging feature. | ||
""" | ||
|
||
alias CodeCorps.{Helpers.Query, Message, Messages, Repo} | ||
alias Ecto.{Changeset, Queryable} | ||
|
||
@doc ~S""" | ||
Lists pre-scoped `CodeCorps.Message` records filtered by parameters. | ||
""" | ||
@spec list(Queryable.t, map) :: list(Message.t) | ||
def list(scope, %{} = params) do | ||
scope | ||
|> Query.id_filter(params) | ||
|> Messages.Query.author_filter(params) | ||
|> Messages.Query.project_filter(params) | ||
|> Repo.all() | ||
end | ||
|
||
@doc ~S""" | ||
Creates a `CodeCorps.Message` from a set of parameters. | ||
""" | ||
@spec create(map) :: {:ok, Message.t} | {:error, Changeset.t} | ||
def create(%{} = params) do | ||
%Message{} | ||
|> Message.changeset(params) | ||
|> Repo.insert() | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
defmodule CodeCorps.Messages.Query do | ||
@moduledoc ~S""" | ||
Holds helpers to query `CodeCorps.Message` records using a map of params. | ||
""" | ||
|
||
import Ecto.Query, only: [where: 3] | ||
|
||
alias Ecto.Queryable | ||
|
||
@doc ~S""" | ||
Narrows down a `CodeCorps.Message` query by `author_id`, if specified in a | ||
params map | ||
""" | ||
@spec author_filter(Queryable.t, map) :: Queryable.t | ||
def author_filter(queryable, %{"author_id" => author_id}) do | ||
queryable |> where([m], m.author_id == ^author_id) | ||
end | ||
def author_filter(queryable, %{}), do: queryable | ||
|
||
@doc ~S""" | ||
Narrows down a `CodeCorps.Message` query by `project_id`, if specified in a | ||
params map | ||
""" | ||
@spec project_filter(Queryable.t, map) :: Queryable.t | ||
def project_filter(queryable, %{"project_id" => project_id}) do | ||
queryable |> where([m], m.project_id == ^project_id) | ||
end | ||
def project_filter(queryable, %{}), do: queryable | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,57 @@ | ||
defmodule CodeCorps.Message do | ||
@moduledoc """ | ||
A message sent from a project to a user or from a user to a project. | ||
The author does not need to be a member of the project in order to send a | ||
message to the project. | ||
No recipient will be defined for the message. The recipient is defined at the | ||
level of the `CodeCorps.Conversation`. | ||
A message may be used as a broadcast to a number of users. A message MAY | ||
therefore have many conversations associated with it. | ||
""" | ||
|
||
use CodeCorps.Model | ||
alias CodeCorps.Message | ||
|
||
@type t :: %__MODULE__{} | ||
|
||
schema "messages" do | ||
field :body, :string | ||
field :initiated_by, :string | ||
field :subject, :string | ||
|
||
belongs_to :author, CodeCorps.User | ||
belongs_to :project, CodeCorps.Project | ||
|
||
timestamps() | ||
end | ||
|
||
@doc false | ||
@spec changeset(Message.t, map) :: Ecto.Changeset.t | ||
def changeset(%Message{} = message, attrs) do | ||
message | ||
|> cast(attrs, [:body, :initiated_by, :subject]) | ||
|> validate_required([:body, :initiated_by]) | ||
|> validate_inclusion(:initiated_by, initiated_by_sources()) | ||
|> require_subject_if_admin() | ||
end | ||
|
||
# validate subject only if initiated_by "admin" | ||
@spec require_subject_if_admin(Ecto.Changeset.t) :: Ecto.Changeset.t | ||
defp require_subject_if_admin(changeset) do | ||
initiated_by = changeset |> Ecto.Changeset.get_field(:initiated_by) | ||
changeset |> do_require_subject_if_admin(initiated_by) | ||
end | ||
|
||
defp do_require_subject_if_admin(changeset, "admin") do | ||
changeset |> validate_required(:subject) | ||
end | ||
defp do_require_subject_if_admin(changeset, _), do: changeset | ||
|
||
@spec initiated_by_sources :: list(String.t) | ||
defp initiated_by_sources do | ||
~w{ admin user } | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,43 @@ | ||
defmodule CodeCorps.Policy.Message do | ||
@moduledoc """ | ||
Handles `User` authorization of actions on `Message` records | ||
""" | ||
|
||
import CodeCorps.Policy.Helpers, only: [administered_by?: 2, get_project: 1] | ||
import Ecto.Query | ||
|
||
alias CodeCorps.{Message, Project, ProjectUser, User, Repo} | ||
|
||
@spec scope(Ecto.Queryable.t, User.t) :: Ecto.Queryable.t | ||
def scope(queryable, %User{admin: true}), do: queryable | ||
def scope(queryable, %User{id: id}) do | ||
projects_administered_by_user_ids = | ||
Project | ||
|> join(:inner, [p], pu in ProjectUser, pu.project_id == p.id) | ||
|> where([_p, pu], pu.user_id == ^id) | ||
|> where([_p, pu], pu.role in ~w(admin owner)) | ||
|> select([p], p.id) | ||
|> Repo.all | ||
|
||
queryable | ||
|> where([m], m.author_id == ^id) | ||
|> or_where([m], m.project_id in ^projects_administered_by_user_ids) | ||
end | ||
|
||
def show?(%User{id: user_id}, %{initiated_by: "user", author_id: author_id}) | ||
when user_id == author_id do | ||
true | ||
end | ||
def show?(%User{} = user, %Message{} = message) do | ||
message |> get_project() |> administered_by?(user) | ||
end | ||
def show?(_, _), do: false | ||
|
||
def create?(%User{id: id}, %{"initiated_by" => "user", "author_id" => author_id}) when id === author_id do | ||
true | ||
end | ||
def create?(%User{} = user, %{"initiated_by" => "admin", "project_id" => _} = params) do | ||
params |> get_project() |> administered_by?(user) | ||
end | ||
def create?(_, _), do: false | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,48 @@ | ||
defmodule CodeCorpsWeb.MessageController do | ||
@moduledoc false | ||
use CodeCorpsWeb, :controller | ||
|
||
alias CodeCorps.{ | ||
Message, | ||
Messages, | ||
User | ||
} | ||
|
||
action_fallback CodeCorpsWeb.FallbackController | ||
plug CodeCorpsWeb.Plug.DataToAttributes | ||
plug CodeCorpsWeb.Plug.IdsToIntegers | ||
|
||
@spec index(Conn.t, map) :: Conn.t | ||
def index(%Conn{} = conn, %{} = params) do | ||
with %User{} = current_user <- conn |> CodeCorps.Guardian.Plug.current_resource, | ||
messages <- Message |> Policy.scope(current_user) |> Messages.list(params) do | ||
conn |> render("index.json-api", data: messages) | ||
end | ||
end | ||
|
||
@spec show(Conn.t, map) :: Conn.t | ||
def show(%Conn{} = conn, %{"id" => id}) do | ||
with %User{} = current_user <- conn |> CodeCorps.Guardian.Plug.current_resource, | ||
%Message{} = message <- Message |> Repo.get(id), | ||
{:ok, :authorized} <- current_user |> Policy.authorize(:show, message, %{}) do | ||
conn |> render("show.json-api", data: message) | ||
end | ||
end | ||
|
||
@spec create(Plug.Conn.t, map) :: Conn.t | ||
def create(%Conn{} = conn, %{} = params) do | ||
with %User{} = current_user <- conn |> CodeCorps.Guardian.Plug.current_resource, | ||
{:ok, :authorized} <- current_user |> Policy.authorize(:create, %Message{}, params), | ||
{:ok, %Message{} = message} <- Messages.create(params), | ||
message <- preload(message) | ||
do | ||
conn |> put_status(:created) |> render("show.json-api", data: message) | ||
end | ||
end | ||
|
||
@preloads [:author, :project] | ||
|
||
def preload(data) do | ||
Repo.preload(data, @preloads) | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
defmodule CodeCorpsWeb.MessageView do | ||
@moduledoc false | ||
use CodeCorpsWeb, :view | ||
use JaSerializer.PhoenixView | ||
|
||
attributes [:body, :initiated_by, :inserted_at, :subject, :updated_at] | ||
|
||
has_one :author, type: "user", field: :author_id | ||
has_one :project, type: "project", field: :project_id | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
defmodule CodeCorps.Repo.Migrations.CreateMessages do | ||
use Ecto.Migration | ||
|
||
def change do | ||
create table(:messages) do | ||
add :body, :text | ||
add :initiated_by, :string | ||
add :subject, :text | ||
add :author_id, references(:users, on_delete: :nothing) | ||
add :project_id, references(:projects, on_delete: :nothing) | ||
|
||
timestamps() | ||
end | ||
|
||
create index(:messages, [:author_id]) | ||
create index(:messages, [:initiated_by]) | ||
create index(:messages, [:project_id]) | ||
end | ||
end |
Oops, something went wrong.