From 247052963bceee41ed13bd2ae4b794f6717740b3 Mon Sep 17 00:00:00 2001 From: Jon Gautsch Date: Wed, 3 Jan 2024 09:35:41 -0800 Subject: [PATCH 1/4] support deprecated input object fields in introspection query --- lib/absinthe/type/built_ins/introspection.ex | 69 ++++++++++++++-- test/absinthe/introspection_test.exs | 87 ++++++++++++++++++++ test/support/fixtures/contact_schema.ex | 1 + 3 files changed, 151 insertions(+), 6 deletions(-) diff --git a/lib/absinthe/type/built_ins/introspection.ex b/lib/absinthe/type/built_ins/introspection.ex index 15ec76fe1a..3529876d27 100644 --- a/lib/absinthe/type/built_ins/introspection.ex +++ b/lib/absinthe/type/built_ins/introspection.ex @@ -69,10 +69,22 @@ defmodule Absinthe.Type.BuiltIns.Introspection do field :args, type: non_null(list_of(non_null(:__inputvalue))), - resolve: fn _, %{source: source} -> + args: [ + include_deprecated: [ + type: :boolean, + default_value: false + ] + ], + resolve: fn %{include_deprecated: show_deprecated}, %{source: source} -> args = source.args - |> Map.values() + |> Enum.flat_map(fn {_, %{deprecation: is_deprecated} = value} -> + if !is_deprecated || (is_deprecated && show_deprecated) do + [value] + else + [] + end + end) |> Enum.sort_by(& &1.identifier) {:ok, args} @@ -191,11 +203,24 @@ defmodule Absinthe.Type.BuiltIns.Introspection do field :input_fields, type: list_of(non_null(:__inputvalue)), + args: [ + include_deprecated: [ + type: :boolean, + default_value: false + ] + ], resolve: fn - _, %{source: %Absinthe.Type.InputObject{fields: fields}} -> + %{include_deprecated: show_deprecated}, + %{source: %Absinthe.Type.InputObject{fields: fields}} -> input_fields = fields - |> Map.values() + |> Enum.flat_map(fn {_, %{deprecation: is_deprecated} = value} -> + if !is_deprecated || (is_deprecated && show_deprecated) do + [value] + else + [] + end + end) |> Enum.sort_by(& &1.identifier) {:ok, input_fields} @@ -226,10 +251,22 @@ defmodule Absinthe.Type.BuiltIns.Introspection do field :args, type: non_null(list_of(non_null(:__inputvalue))), - resolve: fn _, %{source: %{args: args}} -> + args: [ + include_deprecated: [ + type: :boolean, + default_value: false + ] + ], + resolve: fn %{include_deprecated: show_deprecated}, %{source: %{args: args}} -> args = args - |> Map.values() + |> Enum.flat_map(fn {_, %{deprecation: is_deprecated} = value} -> + if !is_deprecated || (is_deprecated && show_deprecated) do + [value] + else + [] + end + end) |> Enum.sort_by(& &1.identifier) {:ok, args} @@ -299,6 +336,26 @@ defmodule Absinthe.Type.BuiltIns.Introspection do _, %{source: _} -> {:ok, nil} end + + field :is_deprecated, + type: non_null(:boolean), + resolve: fn + _, %{source: %{deprecation: nil}} -> + {:ok, false} + + _, _ -> + {:ok, true} + end + + field :deprecation_reason, + type: :string, + resolve: fn + _, %{source: %{deprecation: nil}} -> + {:ok, nil} + + _, %{source: %{deprecation: dep}} -> + {:ok, dep.reason} + end end object :__enumvalue, name: "__EnumValue" do diff --git a/test/absinthe/introspection_test.exs b/test/absinthe/introspection_test.exs index 3575efb9d1..c2e5dd1eba 100644 --- a/test/absinthe/introspection_test.exs +++ b/test/absinthe/introspection_test.exs @@ -278,6 +278,93 @@ defmodule Absinthe.IntrospectionTest do assert !match?({:ok, %{data: %{"__type" => %{"fields" => _}}}}, result) end + test "can include deprecated fields based on an arg" do + result = + """ + { + __type(name: "ProfileInput") { + kind + name + description + inputFields(includeDeprecated: true) { + name + description + type { + kind + name + ofType { + kind + name + } + } + defaultValue + isDeprecated + deprecationReason + } + } + } + """ + |> run(Absinthe.Fixtures.ContactSchema) + + assert_result( + {:ok, + %{ + data: %{ + "__type" => %{ + "description" => "The basic details for a person", + "inputFields" => [ + %{ + "defaultValue" => nil, + "description" => nil, + "name" => "address", + "type" => %{ + "kind" => "SCALAR", + "name" => "String", + "ofType" => nil + }, + "deprecationReason" => "change of privacy policy", + "isDeprecated" => true + }, + %{ + "defaultValue" => "43", + "description" => "The person's age", + "name" => "age", + "type" => %{"kind" => "SCALAR", "name" => "Int", "ofType" => nil}, + "deprecationReason" => nil, + "isDeprecated" => false + }, + %{ + "defaultValue" => nil, + "description" => nil, + "name" => "code", + "type" => %{ + "kind" => "NON_NULL", + "name" => nil, + "ofType" => %{"kind" => "SCALAR", "name" => "String"} + }, + "deprecationReason" => nil, + "isDeprecated" => false + }, + %{ + "defaultValue" => "\"Janet\"", + "description" => "The person's name", + "name" => "name", + "type" => %{"kind" => "SCALAR", "name" => "String", "ofType" => nil}, + "deprecationReason" => nil, + "isDeprecated" => false + } + ], + "kind" => "INPUT_OBJECT", + "name" => "ProfileInput" + } + } + }}, + result + ) + + assert !match?({:ok, %{data: %{"__type" => %{"fields" => _}}}}, result) + end + defmodule ComplexDefaultSchema do use Absinthe.Schema diff --git a/test/support/fixtures/contact_schema.ex b/test/support/fixtures/contact_schema.ex index ef0d247783..f2658ca74a 100644 --- a/test/support/fixtures/contact_schema.ex +++ b/test/support/fixtures/contact_schema.ex @@ -75,6 +75,7 @@ defmodule Absinthe.Fixtures.ContactSchema do field :code, type: non_null(:string) field :name, type: :string, description: "The person's name", default_value: "Janet" field :age, type: :integer, description: "The person's age", default_value: 43 + field :address, type: :string, deprecate: "change of privacy policy" end interface :named_entity do From 2f17002849281408a5e781324a73f3639377117c Mon Sep 17 00:00:00 2001 From: Jon Gautsch Date: Thu, 4 Jan 2024 14:04:58 -0800 Subject: [PATCH 2/4] use Map.values() |> Enum.filter() instead of Enum.flat_map() --- lib/absinthe/type/built_ins/introspection.ex | 36 +++++++------------- 1 file changed, 12 insertions(+), 24 deletions(-) diff --git a/lib/absinthe/type/built_ins/introspection.ex b/lib/absinthe/type/built_ins/introspection.ex index 3529876d27..88af4fb8de 100644 --- a/lib/absinthe/type/built_ins/introspection.ex +++ b/lib/absinthe/type/built_ins/introspection.ex @@ -78,12 +78,9 @@ defmodule Absinthe.Type.BuiltIns.Introspection do resolve: fn %{include_deprecated: show_deprecated}, %{source: source} -> args = source.args - |> Enum.flat_map(fn {_, %{deprecation: is_deprecated} = value} -> - if !is_deprecated || (is_deprecated && show_deprecated) do - [value] - else - [] - end + |> Map.values() + |> Enum.filter(fn %{deprecation: is_deprecated} -> + !is_deprecated || (is_deprecated && show_deprecated) end) |> Enum.sort_by(& &1.identifier) @@ -186,12 +183,9 @@ defmodule Absinthe.Type.BuiltIns.Introspection do %{include_deprecated: show_deprecated}, %{source: %Absinthe.Type.Enum{values: values}} -> result = values - |> Enum.flat_map(fn {_, %{deprecation: is_deprecated} = value} -> - if !is_deprecated || (is_deprecated && show_deprecated) do - [value] - else - [] - end + |> Map.values() + |> Enum.filter(fn %{deprecation: is_deprecated} -> + !is_deprecated || (is_deprecated && show_deprecated) end) |> Enum.sort_by(& &1.value) @@ -214,12 +208,9 @@ defmodule Absinthe.Type.BuiltIns.Introspection do %{source: %Absinthe.Type.InputObject{fields: fields}} -> input_fields = fields - |> Enum.flat_map(fn {_, %{deprecation: is_deprecated} = value} -> - if !is_deprecated || (is_deprecated && show_deprecated) do - [value] - else - [] - end + |> Map.values() + |> Enum.filter(fn %{deprecation: is_deprecated} -> + !is_deprecated || (is_deprecated && show_deprecated) end) |> Enum.sort_by(& &1.identifier) @@ -260,12 +251,9 @@ defmodule Absinthe.Type.BuiltIns.Introspection do resolve: fn %{include_deprecated: show_deprecated}, %{source: %{args: args}} -> args = args - |> Enum.flat_map(fn {_, %{deprecation: is_deprecated} = value} -> - if !is_deprecated || (is_deprecated && show_deprecated) do - [value] - else - [] - end + |> Map.values() + |> Enum.filter(fn %{deprecation: is_deprecated} -> + !is_deprecated || (is_deprecated && show_deprecated) end) |> Enum.sort_by(& &1.identifier) From 53dd591d00b1b112b369ec0452e68d9e0feab002 Mon Sep 17 00:00:00 2001 From: Jon Gautsch Date: Thu, 4 Jan 2024 14:12:27 -0800 Subject: [PATCH 3/4] simplify boolean expressions Because `!is_deprecated || (is_deprecated && show_deprecated)` is equivalent to just `!is_deprecated || show_deprecated` --- lib/absinthe/type/built_ins/introspection.ex | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/lib/absinthe/type/built_ins/introspection.ex b/lib/absinthe/type/built_ins/introspection.ex index 88af4fb8de..9d676a06bc 100644 --- a/lib/absinthe/type/built_ins/introspection.ex +++ b/lib/absinthe/type/built_ins/introspection.ex @@ -80,7 +80,7 @@ defmodule Absinthe.Type.BuiltIns.Introspection do source.args |> Map.values() |> Enum.filter(fn %{deprecation: is_deprecated} -> - !is_deprecated || (is_deprecated && show_deprecated) + !is_deprecated || show_deprecated end) |> Enum.sort_by(& &1.identifier) @@ -122,7 +122,7 @@ defmodule Absinthe.Type.BuiltIns.Introspection do Absinthe.Type.introspection?(field) -> [] - !is_deprecated || (is_deprecated && show_deprecated) -> + !is_deprecated || show_deprecated -> [field] true -> @@ -185,7 +185,7 @@ defmodule Absinthe.Type.BuiltIns.Introspection do values |> Map.values() |> Enum.filter(fn %{deprecation: is_deprecated} -> - !is_deprecated || (is_deprecated && show_deprecated) + !is_deprecated || show_deprecated end) |> Enum.sort_by(& &1.value) @@ -210,7 +210,7 @@ defmodule Absinthe.Type.BuiltIns.Introspection do fields |> Map.values() |> Enum.filter(fn %{deprecation: is_deprecated} -> - !is_deprecated || (is_deprecated && show_deprecated) + !is_deprecated || show_deprecated end) |> Enum.sort_by(& &1.identifier) @@ -253,7 +253,7 @@ defmodule Absinthe.Type.BuiltIns.Introspection do args |> Map.values() |> Enum.filter(fn %{deprecation: is_deprecated} -> - !is_deprecated || (is_deprecated && show_deprecated) + !is_deprecated || show_deprecated end) |> Enum.sort_by(& &1.identifier) From 2323cbea7b0f869dff91a2046b6fac84d6e28ab9 Mon Sep 17 00:00:00 2001 From: Jon Gautsch Date: Thu, 4 Jan 2024 20:26:36 -0800 Subject: [PATCH 4/4] refactor out a defp filter_deprecated/2 to reduce duplication --- lib/absinthe/type/built_ins/introspection.ex | 37 +++++++------------- 1 file changed, 13 insertions(+), 24 deletions(-) diff --git a/lib/absinthe/type/built_ins/introspection.ex b/lib/absinthe/type/built_ins/introspection.ex index 9d676a06bc..61544a13ab 100644 --- a/lib/absinthe/type/built_ins/introspection.ex +++ b/lib/absinthe/type/built_ins/introspection.ex @@ -79,9 +79,7 @@ defmodule Absinthe.Type.BuiltIns.Introspection do args = source.args |> Map.values() - |> Enum.filter(fn %{deprecation: is_deprecated} -> - !is_deprecated || show_deprecated - end) + |> filter_deprecated(show_deprecated) |> Enum.sort_by(& &1.identifier) {:ok, args} @@ -117,18 +115,9 @@ defmodule Absinthe.Type.BuiltIns.Introspection do when str in [Absinthe.Type.Object, Absinthe.Type.Interface] -> result = fields - |> Enum.flat_map(fn {_, %{deprecation: is_deprecated} = field} -> - cond do - Absinthe.Type.introspection?(field) -> - [] - - !is_deprecated || show_deprecated -> - [field] - - true -> - [] - end - end) + |> Map.values() + |> filter_deprecated(show_deprecated) + |> Enum.filter(&(!Absinthe.Type.introspection?(&1))) |> Enum.sort_by(& &1.identifier) {:ok, result} @@ -184,9 +173,7 @@ defmodule Absinthe.Type.BuiltIns.Introspection do result = values |> Map.values() - |> Enum.filter(fn %{deprecation: is_deprecated} -> - !is_deprecated || show_deprecated - end) + |> filter_deprecated(show_deprecated) |> Enum.sort_by(& &1.value) {:ok, result} @@ -209,9 +196,7 @@ defmodule Absinthe.Type.BuiltIns.Introspection do input_fields = fields |> Map.values() - |> Enum.filter(fn %{deprecation: is_deprecated} -> - !is_deprecated || show_deprecated - end) + |> filter_deprecated(show_deprecated) |> Enum.sort_by(& &1.identifier) {:ok, input_fields} @@ -252,9 +237,7 @@ defmodule Absinthe.Type.BuiltIns.Introspection do args = args |> Map.values() - |> Enum.filter(fn %{deprecation: is_deprecated} -> - !is_deprecated || show_deprecated - end) + |> filter_deprecated(show_deprecated) |> Enum.sort_by(& &1.identifier) {:ok, args} @@ -409,4 +392,10 @@ defmodule Absinthe.Type.BuiltIns.Introspection do inspect(Absinthe.Type.Scalar.serialize(sc, value)) end end + + defp filter_deprecated(values, show_deprecated) do + Enum.filter(values, fn %{deprecation: is_deprecated} -> + !is_deprecated || show_deprecated + end) + end end