Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions lib/code_corps/messages/messages.ex
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,10 @@ defmodule CodeCorps.Messages do
Conversation |> Repo.get(id)
end

def update_conversation(conversation, params) do
conversation |> Conversation.update_changeset(params) |> Repo.update
end

@doc ~S"""
Gets a `CodeCorps.ConversationPart` record
"""
Expand Down
10 changes: 10 additions & 0 deletions lib/code_corps/model/conversation.ex
Original file line number Diff line number Diff line change
Expand Up @@ -26,4 +26,14 @@ defmodule CodeCorps.Conversation do

timestamps()
end

def update_changeset(struct, %{"status" => status} = params) do
struct
|> cast(params, [:status])
|> validate_inclusion(:status, statuses())
end

defp statuses do
~w{ open closed }
end
end
6 changes: 6 additions & 0 deletions lib/code_corps/policy/conversation.ex
Original file line number Diff line number Diff line change
Expand Up @@ -41,4 +41,10 @@ defmodule CodeCorps.Policy.Conversation do
conversation |> get_message() |> get_project() |> administered_by?(user)
end
def show?(_, _), do: false

def update?(%User{admin: true}, _conversation), do: true
def update?(%User{} = user, %Conversation{} = conversation) do
conversation |> get_message() |> get_project() |> administered_by?(user)
end
def update?(_, _), do: false
end
1 change: 1 addition & 0 deletions lib/code_corps/policy/policy.ex
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@ defmodule CodeCorps.Policy do

# Conversation
defp can?(%User{} = current_user, :show, %Conversation{} = conversation, %{}), do: Policy.Conversation.show?(current_user, conversation)
defp can?(%User{} = current_user, :update, %Conversation{} = conversation, %{}), do: Policy.Conversation.update?(current_user, conversation)

# ConversationPart
defp can?(%User{} = current_user, :create, %ConversationPart{}, %{} = params), do: Policy.ConversationPart.create?(current_user, params)
Expand Down
12 changes: 12 additions & 0 deletions lib/code_corps_web/controllers/conversation_controller.ex
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,18 @@ defmodule CodeCorpsWeb.ConversationController do
end
end

@spec update(Conn.t, map) :: Conn.t
def update(%Conn{} = conn, %{"id" => id} = params) do
with %Conversation{} = conversation <- Messages.get_conversation(id) |> preload(),
%User{} = current_user <- conn |> CodeCorps.Guardian.Plug.current_resource,
{:ok, :authorized} <- current_user |> Policy.authorize(:update, conversation),
{:ok, %Conversation{} = updated_conversation} <- conversation |> Messages.update_conversation(params)
do

conn |> render("show.json-api", data: updated_conversation)
end
end

@preloads [:conversation_parts, :message, :user]

def preload(data) do
Expand Down
2 changes: 1 addition & 1 deletion lib/code_corps_web/router.ex
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ defmodule CodeCorpsWeb.Router do

resources "/categories", CategoryController, only: [:create, :update]
resources "/comments", CommentController, only: [:create, :update]
resources "/conversations", ConversationController, only: [:index, :show]
resources "/conversations", ConversationController, only: [:index, :show, :update]
resources "/conversation-parts", ConversationPartController, only: [:index, :show, :create]
resources "/donation-goals", DonationGoalController, only: [:create, :update, :delete]
post "/oauth/github", UserController, :github_oauth
Expand Down
44 changes: 43 additions & 1 deletion test/lib/code_corps/policy/conversation_test.exs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
defmodule CodeCorps.Policy.ConversationTest do
use CodeCorps.PolicyCase

import CodeCorps.Policy.Conversation, only: [scope: 2, show?: 2]
import CodeCorps.Policy.Conversation, only: [scope: 2, show?: 2, update?: 2]

alias CodeCorps.{Conversation, Repo}

Expand Down Expand Up @@ -113,4 +113,46 @@ defmodule CodeCorps.Policy.ConversationTest do
assert show?(user, conversation)
end
end

describe "update?" do
test "returns true when user is admin" do
user = insert(:user, admin: true)
message = insert(:message)
conversation = insert(:conversation, message: message, user: user)

assert update?(user, conversation)
end

test "returns false when user is a pending project member" do
%{project: project, user: user} = insert(:project_user, role: "pending")
message = insert(:message, project: project)
conversation = insert(:conversation, message: message)

refute update?(user, conversation)
end

test "returns false when user is a project contributor" do
%{project: project, user: user} = insert(:project_user, role: "contributor")
message = insert(:message, project: project)
conversation = insert(:conversation, message: message)

refute update?(user, conversation)
end

test "returns true when user is a project admin" do
%{project: project, user: user} = insert(:project_user, role: "admin")
message = insert(:message, project: project)
conversation = insert(:conversation, message: message)

assert update?(user, conversation)
end

test "returns true when user is project owner" do
%{project: project, user: user} = insert(:project_user, role: "owner")
message = insert(:message, project: project)
conversation = insert(:conversation, message: message)

assert update?(user, conversation)
end
end
end
Original file line number Diff line number Diff line change
Expand Up @@ -65,4 +65,39 @@ defmodule CodeCorpsWeb.ConversationControllerTest do
assert conn |> request_show(conversation) |> json_response(403)
end
end

describe "update" do
@tag authenticated: :admin
test "updates and renders chosen resource when data is valid", %{conn: conn, current_user: user} do
%{project: project} = insert(:project_user, role: "admin", user: user)
message_on_user_administered_project = insert(:message, project: project)
conversation_on_user_administered_project =
insert(:conversation, message: message_on_user_administered_project)

data =
conn
|> request_update(conversation_on_user_administered_project, %{status: "closed"})
|> json_response(200)
|> Map.get("data")

assert data["attributes"]["status"] == "closed"
end

@tag authenticated: :admin
test "renders 422 when data is invalid", %{conn: conn, current_user: current_user} do
conversation = insert(:conversation, user: current_user)
assert conn |> request_update(conversation, %{status: "wat"}) |> json_response(422)
end

test "renders 401 when unauthenticated", %{conn: conn} do
assert conn |> request_update |> json_response(401)
end

@tag :authenticated
test "does not update resource and renders 403 when not authorized", %{conn: conn} do
user = insert(:user)
insert(:conversation, user: user)
assert conn |> request_update() |> json_response(403)
end
end
end