Skip to content

Commit 0ba3ab9

Browse files
committed
Add scope and index
1 parent 33d2517 commit 0ba3ab9

File tree

8 files changed

+177
-24
lines changed

8 files changed

+177
-24
lines changed

lib/code_corps/messages/messages.ex

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,14 @@ defmodule CodeCorps.Messages do
3636
|> Repo.all()
3737
end
3838

39+
@doc ~S"""
40+
Lists pre-scoped `CodeCorps.ConversationPart` records filtered by parameters
41+
"""
42+
@spec list_parts(Queryable.t, map) :: list(Conversation.t)
43+
def list_parts(scope, %{} = _params) do
44+
scope |> Repo.all()
45+
end
46+
3947
@doc ~S"""
4048
Gets a `CodeCorps.Conversation` record
4149
"""

lib/code_corps/policy/conversation_part.ex

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,23 @@ defmodule CodeCorps.Policy.ConversationPart do
88
only: [
99
administered_by?: 2, get_conversation: 1, get_message: 1, get_project: 1
1010
]
11-
12-
alias CodeCorps.{Conversation, ConversationPart, User}
11+
import Ecto.Query
12+
13+
alias CodeCorps.{Conversation, ConversationPart, Policy, Repo, User}
14+
15+
@spec scope(Ecto.Queryable.t, User.t) :: Ecto.Queryable.t
16+
def scope(queryable, %User{admin: true}), do: queryable
17+
def scope(queryable, %User{id: id} = current_user) do
18+
scoped_conversation_ids =
19+
Conversation
20+
|> Policy.Conversation.scope(current_user)
21+
|> select([c], c.id)
22+
|> Repo.all()
23+
24+
queryable
25+
|> where(author_id: ^id)
26+
|> or_where([cp], cp.conversation_id in ^scoped_conversation_ids)
27+
end
1328

1429
def create?(%User{} = user, %{"conversation_id" => _} = params) do
1530
authorize(user, params)

lib/code_corps/policy/policy.ex

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@ defmodule CodeCorps.Policy do
6161
@spec scope(module, User.t) :: Ecto.Queryable.t
6262
def scope(Message, %User{} = current_user), do: Message |> Policy.Message.scope(current_user)
6363
def scope(Conversation, %User{} = current_user), do: Conversation |> Policy.Conversation.scope(current_user)
64+
def scope(ConversationPart, %User{} = current_user), do: ConversationPart |> Policy.ConversationPart.scope(current_user)
6465

6566
@spec can?(User.t, atom, struct, map) :: boolean
6667

lib/code_corps_web/controllers/conversation_part_controller.ex

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,14 @@ defmodule CodeCorpsWeb.ConversationPartController do
1212
plug CodeCorpsWeb.Plug.DataToAttributes
1313
plug CodeCorpsWeb.Plug.IdsToIntegers
1414

15+
@spec index(Conn.t, map) :: Conn.t
16+
def index(%Conn{} = conn, %{} = params) do
17+
with %User{} = current_user <- conn |> CodeCorps.Guardian.Plug.current_resource,
18+
conversation_parts <- ConversationPart |> Policy.scope(current_user) |> Messages.list_parts(params) do
19+
conn |> render("index.json-api", data: conversation_parts)
20+
end
21+
end
22+
1523
@spec create(Plug.Conn.t, map) :: Conn.t
1624
def create(%Conn{} = conn, %{} = params) do
1725
with %User{} = current_user <- conn |> CodeCorps.Guardian.Plug.current_resource,

lib/code_corps_web/router.ex

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,7 @@ defmodule CodeCorpsWeb.Router do
7272
resources "/categories", CategoryController, only: [:create, :update]
7373
resources "/comments", CommentController, only: [:create, :update]
7474
resources "/conversations", ConversationController, only: [:index, :show]
75-
resources "/conversation-parts", ConversationPartController, only: [:create, :show]
75+
resources "/conversation-parts", ConversationPartController, only: [:index, :show, :create]
7676
resources "/donation-goals", DonationGoalController, only: [:create, :update, :delete]
7777
post "/oauth/github", UserController, :github_oauth
7878
resources "/github-app-installations", GithubAppInstallationController, only: [:create]

test/lib/code_corps/messages/messages_test.exs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -283,6 +283,13 @@ defmodule CodeCorps.MessagesTest do
283283
end
284284
end
285285

286+
describe "list_parts/2" do
287+
test "returns all records by default" do
288+
insert_list(3, :conversation_part)
289+
assert ConversationPart |> Messages.list_parts(%{}) |> Enum.count == 3
290+
end
291+
end
292+
286293
describe "get_conversation/1" do
287294
test "gets a single conversation" do
288295
conversation = insert(:conversation)

test/lib/code_corps/policy/conversation_part_test.exs

Lines changed: 77 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
defmodule CodeCorps.Policy.ConversationPartTest do
22
use CodeCorps.PolicyCase
33

4-
import CodeCorps.Policy.ConversationPart, only: [create?: 2, show?: 2]
4+
import CodeCorps.Policy.ConversationPart, only: [create?: 2, scope: 2, show?: 2]
5+
6+
alias CodeCorps.{ConversationPart, Repo}
57

68
defp params(user, conversation) do
79
%{
@@ -11,6 +13,80 @@ defmodule CodeCorps.Policy.ConversationPartTest do
1113
}
1214
end
1315

16+
describe "scope" do
17+
test "returns all records for admin user" do
18+
insert_list(3, :conversation_part)
19+
user = insert(:user, admin: true)
20+
21+
assert ConversationPart |> scope(user) |> Repo.all |> Enum.count == 3
22+
end
23+
24+
test "returns records where user is the author or they administer the project" do
25+
user = insert(:user, admin: false)
26+
27+
%{project: project_user_applied_to} =
28+
insert(:project_user, user: user, role: "pending")
29+
30+
%{project: project_user_contributes_to} =
31+
insert(:project_user, user: user, role: "contributor")
32+
33+
%{project: project_user_administers} =
34+
insert(:project_user, user: user, role: "admin")
35+
36+
%{project: project_user_owns} =
37+
insert(:project_user, user: user, role: "owner")
38+
39+
message_in_project_applied_to =
40+
insert(:message, project: project_user_applied_to)
41+
42+
message_in_contributing_project =
43+
insert(:message, project: project_user_contributes_to)
44+
45+
message_in_administered_project =
46+
insert(:message, project: project_user_administers)
47+
48+
message_in_owned_project =
49+
insert(:message, project: project_user_owns)
50+
51+
conversation_when_target = insert(:conversation, user: user)
52+
conversation_when_pending =
53+
insert(:conversation, message: message_in_project_applied_to)
54+
conversation_when_contributor =
55+
insert(:conversation, message: message_in_contributing_project)
56+
conversation_when_admin =
57+
insert(:conversation, message: message_in_administered_project)
58+
conversation_when_owner =
59+
insert(:conversation, message: message_in_owned_project)
60+
some_other_conversation = insert(:conversation)
61+
62+
part_in_conversation_when_target =
63+
insert(:conversation_part, conversation: conversation_when_target)
64+
part_in_project_applied_to =
65+
insert(:conversation_part, conversation: conversation_when_pending)
66+
part_in_contributing_project =
67+
insert(:conversation_part, conversation: conversation_when_contributor)
68+
part_in_administered_project =
69+
insert(:conversation_part, conversation: conversation_when_admin)
70+
part_in_owned_project =
71+
insert(:conversation_part, conversation: conversation_when_owner)
72+
part_in_some_other_conversation =
73+
insert(:conversation_part, conversation: some_other_conversation)
74+
75+
result_ids =
76+
ConversationPart
77+
|> scope(user)
78+
|> Repo.all
79+
|> Enum.map(&Map.get(&1, :id))
80+
81+
assert part_in_conversation_when_target.id in result_ids
82+
refute part_in_project_applied_to.id in result_ids
83+
refute part_in_contributing_project.id in result_ids
84+
assert part_in_administered_project.id in result_ids
85+
assert part_in_owned_project.id in result_ids
86+
refute part_in_some_other_conversation.id in result_ids
87+
end
88+
end
89+
1490
describe "create?" do
1591
test "returns true when user is the target" do
1692
user = insert(:user)

test/lib/code_corps_web/controllers/conversation_part_controller_test.exs

Lines changed: 58 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -9,33 +9,41 @@ defmodule CodeCorpsWeb.ConversationPartControllerTest do
99
body: nil
1010
}
1111

12-
describe "create" do
12+
describe "index" do
1313
@tag :authenticated
14-
test "creates and renders resource when data is valid", %{conn: conn, current_user: user} do
15-
conversation = insert(:conversation, user: user)
16-
attrs = @valid_attrs |> Map.merge(%{author_id: user.id, conversation_id: conversation.id})
14+
test "lists all entries user is authorized to view", %{conn: conn, current_user: user} do
15+
%{project: project} = insert(:project_user, role: "admin", user: user)
16+
message_on_user_administered_project = insert(:message, project: project)
1717

18-
assert conn |> request_create(attrs) |> json_response(201)
19-
end
18+
conversation_on_user_administered_project =
19+
insert(:conversation, message: message_on_user_administered_project)
20+
conversation_part_in_project =
21+
insert(:conversation_part, conversation: conversation_on_user_administered_project)
2022

21-
@tag :authenticated
22-
test "does not create resource and renders 422 when data is invalid", %{
23-
conn: conn,
24-
current_user: user
25-
} do
26-
conversation = insert(:conversation, user: user)
27-
attrs = @invalid_attrs |> Map.merge(%{author_id: user.id, conversation_id: conversation.id})
23+
conversation_by_user = insert(:conversation, user: user)
24+
conversation_part_from_user =
25+
insert(:conversation_part, conversation: conversation_by_user)
2826

29-
assert conn |> request_create(attrs) |> json_response(422)
30-
end
27+
other_conversation = insert(:conversation)
28+
_other_part = insert(:conversation_part, conversation: other_conversation)
3129

32-
test "does not create resource and renders 401 when not authenticated", %{conn: conn} do
33-
assert conn |> request_create |> json_response(401)
30+
conn
31+
|> request_index
32+
|> json_response(200)
33+
|> assert_ids_from_response([
34+
conversation_part_in_project.id,
35+
conversation_part_from_user.id
36+
])
3437
end
3538

36-
@tag :authenticated
37-
test "renders 403 when not authorized", %{conn: conn} do
38-
assert conn |> request_create |> json_response(403)
39+
@tag authenticated: :admin
40+
test "lists all entries if user is admin", %{conn: conn} do
41+
[part_1, part_2] = insert_pair(:conversation_part)
42+
43+
conn
44+
|> request_index
45+
|> json_response(200)
46+
|> assert_ids_from_response([part_1.id, part_2.id])
3947
end
4048
end
4149

@@ -62,4 +70,34 @@ defmodule CodeCorpsWeb.ConversationPartControllerTest do
6270
assert conn |> request_show(conversation_part) |> json_response(403)
6371
end
6472
end
73+
74+
describe "create" do
75+
@tag :authenticated
76+
test "creates and renders resource when data is valid", %{conn: conn, current_user: user} do
77+
conversation = insert(:conversation, user: user)
78+
attrs = @valid_attrs |> Map.merge(%{author_id: user.id, conversation_id: conversation.id})
79+
80+
assert conn |> request_create(attrs) |> json_response(201)
81+
end
82+
83+
@tag :authenticated
84+
test "does not create resource and renders 422 when data is invalid", %{
85+
conn: conn,
86+
current_user: user
87+
} do
88+
conversation = insert(:conversation, user: user)
89+
attrs = @invalid_attrs |> Map.merge(%{author_id: user.id, conversation_id: conversation.id})
90+
91+
assert conn |> request_create(attrs) |> json_response(422)
92+
end
93+
94+
test "does not create resource and renders 401 when not authenticated", %{conn: conn} do
95+
assert conn |> request_create |> json_response(401)
96+
end
97+
98+
@tag :authenticated
99+
test "renders 403 when not authorized", %{conn: conn} do
100+
assert conn |> request_create |> json_response(403)
101+
end
102+
end
65103
end

0 commit comments

Comments
 (0)