Skip to content

Commit dae8789

Browse files
Merge pull request #78 from epochtalk/validate-mutually-exclusive
Validate mutually exclusive
2 parents 1ea71b9 + b9a5d45 commit dae8789

File tree

5 files changed

+90
-3
lines changed

5 files changed

+90
-3
lines changed

lib/epochtalk_server_web/controllers/post.ex

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -151,14 +151,14 @@ defmodule EpochtalkServerWeb.Controllers.Post do
151151
"""
152152
# TODO(akinsey): Implement for completion
153153
# - parser
154-
# - Implement guard in Validate that prevents passing in page and start at the same time
155154
def by_thread(conn, attrs) do
156155
# Parameter Validation
157156
with thread_id <- Validate.cast(attrs, "thread_id", :integer, required: true),
158157
page <- Validate.cast(attrs, "page", :integer, default: 1, min: 1),
159158
start <- Validate.cast(attrs, "start", :integer, min: 1),
160159
limit <- Validate.cast(attrs, "limit", :integer, default: 25, min: 1, max: 100),
161160
desc <- Validate.cast(attrs, "desc", :boolean, default: true),
161+
:ok <- Validate.mutually_exclusive!(attrs, ["page", "start"]),
162162
user <- Guardian.Plug.current_resource(conn),
163163
user_priority <- ACL.get_user_priority(conn),
164164

lib/epochtalk_server_web/controllers/thread.ex

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -43,9 +43,7 @@ defmodule EpochtalkServerWeb.Controllers.Thread do
4343
# TODO(akinsey): Post pre processing, authorizations, image processing, hooks. Things to consider:
4444
# - does html sanitizer need to run on the front end too
4545
# - how do we run the same parser/sanitizer on the elixir back end as the node frontend
46-
# - processing mentions
4746
# - does createImageReferences need to be called here? was this missing from the old code?
48-
# - does updateUserActivity need to be called here? was this missing from the old code?
4947
def create(conn, attrs) do
5048
# authorization checks
5149
with :ok <- ACL.allow!(conn, "threads.create"),

lib/epochtalk_server_web/errors/custom_errors.ex

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@ defmodule EpochtalkServerWeb.CustomErrors do
4949
def exception(value) do
5050
case value do
5151
[] -> %InvalidPayload{}
52+
[message: message] -> %InvalidPayload{message: message}
5253
_ -> %InvalidPayload{message: gen_message(Enum.into(value, %{required: false}))}
5354
end
5455
end

lib/epochtalk_server_web/helpers/validate.ex

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,20 @@ defmodule EpochtalkServerWeb.Helpers.Validate do
66
"""
77
alias EpochtalkServerWeb.CustomErrors.InvalidPayload
88

9+
@doc """
10+
Ensure that `keys` provided in list are mutually exclusive within `attrs` map.
11+
"""
12+
@spec mutually_exclusive!(attrs :: map, keys :: [String.t()]) :: :ok | no_return
13+
def mutually_exclusive!(attrs, keys) when is_map(attrs) and is_list(keys) do
14+
if map_contains_any_two_keys_in_list?(attrs, keys),
15+
do:
16+
raise(InvalidPayload,
17+
message:
18+
"The following payload parameters cannot be passed at the same time: #{Enum.join(keys, ", ")}"
19+
),
20+
else: :ok
21+
end
22+
923
@doc """
1024
Helper used to validate and cast request parameters directly out of the incoming
1125
paylod map (usually a controller function's `attrs` parameter) to the specified type.
@@ -121,6 +135,29 @@ defmodule EpochtalkServerWeb.Helpers.Validate do
121135
end
122136
end
123137

138+
# entrypoint
139+
defp map_contains_any_two_keys_in_list?(map, list),
140+
do: map_contains_any_two_keys_in_list?(map, list, false)
141+
142+
# if map is empty, return false
143+
defp map_contains_any_two_keys_in_list?(map, _list, _key_found?) when map == %{}, do: false
144+
# if list is empty, return false
145+
defp map_contains_any_two_keys_in_list?(_map, [], _key_found?), do: false
146+
# if key_found? is false
147+
defp map_contains_any_two_keys_in_list?(map, [key | keys] = _list, false = _key_found?) do
148+
# check next key with updated key_found?
149+
map_contains_any_two_keys_in_list?(map, keys, Map.has_key?(map, key))
150+
end
151+
152+
# if key_found? is true
153+
defp map_contains_any_two_keys_in_list?(map, [key | keys] = _list, true = key_found?) do
154+
# if current key is in map, return true
155+
if Map.has_key?(map, key),
156+
do: true,
157+
# otherwise, check next key
158+
else: map_contains_any_two_keys_in_list?(map, keys, key_found?)
159+
end
160+
124161
defp to_bool(str, opts) do
125162
case str do
126163
"true" -> true
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,56 @@
11
defmodule Test.EpochtalkServerWeb.Helpers.Validate do
22
use Test.Support.ConnCase, async: true
33
alias EpochtalkServerWeb.Helpers.Validate
4+
alias EpochtalkServerWeb.CustomErrors.InvalidPayload
45
doctest Validate
6+
7+
describe "mutually_exclusive/2" do
8+
test "given cases, returns correct result" do
9+
cases = [
10+
%{
11+
attrs: %{"page" => 1},
12+
keys: ["page", "start"],
13+
result: :ok
14+
},
15+
%{
16+
attrs: %{"start" => 1},
17+
keys: ["page", "start"],
18+
result: :ok
19+
},
20+
%{
21+
attrs: %{"start" => 1},
22+
keys: ["start"],
23+
result: :ok
24+
},
25+
%{
26+
attrs: %{"page" => 1, "start" => 1},
27+
keys: ["page", "start"],
28+
result: :error
29+
},
30+
%{
31+
attrs: %{"page" => 1, "start" => 1, "stop" => 1},
32+
keys: ["page", "start"],
33+
result: :error
34+
},
35+
%{
36+
attrs: %{"page" => 1, "stop" => 1},
37+
keys: ["page", "start", "stop", "enter"],
38+
result: :error
39+
}
40+
]
41+
42+
cases
43+
|> Enum.each(fn
44+
%{attrs: attrs, keys: keys, result: :error} ->
45+
assert_raise InvalidPayload,
46+
~r/^The following payload parameters cannot be passed at the same time:/,
47+
fn ->
48+
Validate.mutually_exclusive!(attrs, keys)
49+
end
50+
51+
%{attrs: attrs, keys: keys, result: :ok} ->
52+
assert Validate.mutually_exclusive!(attrs, keys) == :ok
53+
end)
54+
end
55+
end
556
end

0 commit comments

Comments
 (0)