Skip to content

Commit

Permalink
fix: handle list arguments and add tests
Browse files Browse the repository at this point in the history
  • Loading branch information
DReigada committed Nov 15, 2023
1 parent 2e2123e commit 59797da
Show file tree
Hide file tree
Showing 4 changed files with 90 additions and 8 deletions.
24 changes: 18 additions & 6 deletions lib/absinthe_constraints/phase.ex
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,16 @@ defmodule AbsintheConstraints.Phase do
)
end

# Handles the same nodes as below but for Lists
# TODO: We could also validate the items inside lists with other constraints.
defp handle_node(
%{
input_value: %{normalized: %Absinthe.Blueprint.Input.List{items: value}},
schema_node: %{__private__: private}
} = node
),
do: handle_node_value(node, value, private)

# Handles the following nodes:
# Absinthe.Blueprint.Input.Field
# Absinthe.Blueprint.Input.Argument
Expand All @@ -66,14 +76,18 @@ defmodule AbsintheConstraints.Phase do
input_value: %{normalized: %{value: value}},
schema_node: %{__private__: private}
} = node
) do
),
do: handle_node_value(node, value, private)

# Handle all other nodes
defp handle_node(node), do: node

defp handle_node_value(node, value, private) do
Keyword.get(private, :constraints, [])
|> Enum.flat_map(&handle_constraint(&1, value, node))
|> Enum.reduce(node, &add_error/2)
|> Enum.reduce(node, &Absinthe.Phase.put_error(&2, &1))
end

defp handle_node(node), do: node

defp handle_constraint(config, value, node) do
Validator.handle_constraint(config, value)
|> make_errors(node)
Expand All @@ -89,6 +103,4 @@ defmodule AbsintheConstraints.Phase do
}
end)
end

defp add_error(error, node), do: Absinthe.Phase.put_error(node, error)
end
6 changes: 5 additions & 1 deletion mix.exs
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,11 @@ defmodule AbsintheConstraints.MixProject do

defp deps do
[
{:absinthe, ">= 1.7.0"},
# We require this version for a fix introduced in master that is not yet released.
{:absinthe,
github: "absinthe-graphql/absinthe",
ref: "a0afbe1aa3d7e2d47a88370b78d62fa9c002e72c",
override: true},
{:elixir_uuid, ">= 1.2.1"},
{:ex_doc, "~> 0.14", only: :dev, runtime: false},
{:dialyxir, "~> 1.3", only: [:dev], runtime: false}
Expand Down
2 changes: 1 addition & 1 deletion mix.lock
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
%{
"absinthe": {:hex, :absinthe, "1.7.5", "a15054f05738e766f7cc7fd352887dfd5e61cec371fb4741cca37c3359ff74ac", [:mix], [{:dataloader, "~> 1.0.0 or ~> 2.0", [hex: :dataloader, repo: "hexpm", optional: true]}, {:decimal, "~> 1.0 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: true]}, {:nimble_parsec, "~> 1.2.2 or ~> 1.3.0", [hex: :nimble_parsec, repo: "hexpm", optional: false]}, {:opentelemetry_process_propagator, "~> 0.2.1", [hex: :opentelemetry_process_propagator, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "22a9a38adca26294ad0ee91226168f5d215b401efd770b8a1b8fd9c9b21ec316"},
"absinthe": {:git, "https://github.com/absinthe-graphql/absinthe.git", "a0afbe1aa3d7e2d47a88370b78d62fa9c002e72c", [ref: "a0afbe1aa3d7e2d47a88370b78d62fa9c002e72c"]},
"dialyxir": {:hex, :dialyxir, "1.3.0", "fd1672f0922b7648ff9ce7b1b26fcf0ef56dda964a459892ad15f6b4410b5284", [:mix], [{:erlex, ">= 0.2.6", [hex: :erlex, repo: "hexpm", optional: false]}], "hexpm", "00b2a4bcd6aa8db9dcb0b38c1225b7277dca9bc370b6438715667071a304696f"},
"earmark_parser": {:hex, :earmark_parser, "1.4.33", "3c3fd9673bb5dcc9edc28dd90f50c87ce506d1f71b70e3de69aa8154bc695d44", [:mix], [], "hexpm", "2d526833729b59b9fdb85785078697c72ac5e5066350663e5be6a1182da61b8f"},
"elixir_uuid": {:hex, :elixir_uuid, "1.2.1", "dce506597acb7e6b0daeaff52ff6a9043f5919a4c3315abb4143f0b00378c097", [:mix], [], "hexpm", "f7eba2ea6c3555cea09706492716b0d87397b88946e6380898c2889d68585752"},
Expand Down
66 changes: 66 additions & 0 deletions test/integration/api_test.exs
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
defmodule AbsintheConstraints.Integration.APITest do
use ExUnit.Case, async: true

describe "without a custom default resolver defined" do
defmodule NormalSchema do
use Absinthe.Schema

@prototype_schema AbsintheConstraints.Directive

input_object :input_object do
field(:id, :string, directives: [constraints: [format: "uuid"]])
end

query do
field :test, non_null(:string) do
arg(:list, list_of(:integer), directives: [constraints: [min_items: 2]])
arg(:number, :integer, directives: [constraints: [min: 2]])
resolve(fn _, _ -> {:ok, "asdf"} end)
end
end

mutation do
field(:do_something, :string) do
arg(:id, non_null(:string), directives: [constraints: [format: "uuid"]])
arg(:id_obj, non_null(:input_object))

resolve(fn _, _ -> {:ok, %{status: "ok"}} end)
end
end
end

test "should validate query arguments" do
assert {:ok,
%{
errors: [
%{
message: "\"list\" must have at least 2 items",
locations: [%{line: 1, column: 8}]
},
%{
message: "\"number\" must be greater than or equal to 2",
locations: [%{line: 1, column: 19}]
}
]
}} ==
Absinthe.run("{ test(list: [1], number: 1) }", NormalSchema,
pipeline_modifier: &AbsintheConstraints.Phase.add_to_pipeline/2
)
end

test "should validate mutation arguments" do
assert {:ok,
%{
errors: [
%{message: "\"id\" must be a valid UUID", locations: [%{line: 1, column: 25}]},
%{message: "\"id\" must be a valid UUID", locations: [%{line: 1, column: 46}]}
]
}} ==
Absinthe.run(
"mutation { do_something(id: \"asdf\", id_obj: {id: \"123\"}) }",
NormalSchema,
pipeline_modifier: &AbsintheConstraints.Phase.add_to_pipeline/2
)
end
end
end

0 comments on commit 59797da

Please sign in to comment.