Created
January 4, 2022 07:32
-
-
Save maartenvanvliet/cc3005fdb5950059edc5e0e753bd7c0c to your computer and use it in GitHub Desktop.
Absinthe spec compliant int pipeline modifier
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
defmodule Absinthe.Type.BuiltIns.SpecCompliantInt do | |
use Absinthe.Schema.Notation | |
@moduledoc false | |
scalar :integer, name: "Int" do | |
description """ | |
The `Int` scalar type represents non-fractional signed whole numeric | |
values between `-2^31` and `2^31 - 1` as outlined in the | |
[GraphQl spec](ttps://spec.graphql.org/October2021/#sec-Int). | |
""" | |
serialize &__MODULE__.serialize_int/1 | |
parse parse_with([Absinthe.Blueprint.Input.Integer], &parse_int/1) | |
end | |
@max_int 2_147_483_647 | |
@min_int -2_147_483_648 | |
def serialize_int(value) when is_integer(value) and value >= @min_int and value <= @max_int do | |
value | |
end | |
defp parse_int(value) when is_integer(value) and value >= @min_int and value <= @max_int do | |
{:ok, value} | |
end | |
defp parse_int(_) do | |
:error | |
end | |
# Parse, supporting pulling values out of blueprint Input nodes | |
defp parse_with(node_types, coercion) do | |
fn | |
%{__struct__: str, value: value} -> | |
if Enum.member?(node_types, str) do | |
coercion.(value) | |
else | |
:error | |
end | |
%Absinthe.Blueprint.Input.Null{} -> | |
{:ok, nil} | |
other -> | |
coercion.(other) | |
end | |
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
defmodule Absinthe.Phase.Schema.SpecCompliantInt do | |
@moduledoc false | |
@behaviour Absinthe.Phase | |
alias Absinthe.Blueprint | |
def pipeline(pipeline) do | |
Absinthe.Pipeline.insert_after(pipeline, Absinthe.Phase.Schema.TypeImports, __MODULE__) | |
end | |
@spec run(Blueprint.t(), Keyword.t()) :: {:ok, Blueprint.t()} | |
def run(input, _options \\ []) do | |
node = Blueprint.prewalk(input, &handle_node/1) | |
{:ok, node} | |
end | |
defp handle_node(%Blueprint.Schema.ScalarTypeDefinition{identifier: :integer}) do | |
case ensure_compiled(Absinthe.Type.BuiltIns.SpecCompliantInt) do | |
{:module, module} -> | |
[types] = module.__absinthe_blueprint__.schema_definitions | |
Enum.find(types.type_definitions, &(&1.identifier == :integer)) | |
{:error, reason} -> | |
raise reason | |
end | |
end | |
defp handle_node(node) do | |
node | |
end | |
# Elixir v1.12 includes a Code.ensure_compiled!/1 that tells | |
# the compiler it should only continue if the module is available. | |
# This gives the Elixir compiler more information to address | |
# deadlocks. | |
# TODO: Remove the else clause once we require Elixir v1.12+. | |
@compile {:no_warn_undefined, {Code, :ensure_compiled!, 1}} | |
@dialyzer {:nowarn_function, [ensure_compiled: 1]} | |
defp ensure_compiled(module) do | |
if function_exported?(Code, :ensure_compiled!, 1) do | |
{:module, Code.ensure_compiled!(module)} | |
else | |
Code.ensure_compiled(module) | |
end | |
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
defmodule Absinthe.Phase.Schema.SpecCompliantInt.Test do | |
use Absinthe.Case, async: true | |
defmodule Schema do | |
use Absinthe.Schema | |
@pipeline_modifier Absinthe.Phase.Schema.SpecCompliantInt | |
query do | |
field :parse, :integer do | |
arg :input, :integer | |
resolve fn args, _ -> | |
{:ok, args[:input]} | |
end | |
end | |
end | |
end | |
test "we can override the builtin scalars" do | |
assert {:ok, | |
%{ | |
errors: [ | |
%{ | |
locations: [%{column: 34, line: 1}], | |
message: "Argument \"input\" has invalid value $input." | |
} | |
] | |
}} == | |
Absinthe.run( | |
"query ($input: Integer!) { parse(input: $input) }", | |
Schema, | |
variables: %{ | |
"input" => 9_007_199_254_740_991 | |
} | |
) | |
end | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment