From d9e8017c15aa101f528ac4c4847087c97d3b21b0 Mon Sep 17 00:00:00 2001 From: Brian Underwood Date: Sun, 2 May 2021 21:45:45 +0200 Subject: [PATCH 001/120] Add documentation for batch/item syntax in dataloader To address https://github.com/absinthe-graphql/absinthe/issues/1066 --- lib/absinthe/resolution/helpers.ex | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/lib/absinthe/resolution/helpers.ex b/lib/absinthe/resolution/helpers.ex index 94a42e19de..a060ceebed 100644 --- a/lib/absinthe/resolution/helpers.ex +++ b/lib/absinthe/resolution/helpers.ex @@ -263,6 +263,19 @@ defmodule Absinthe.Resolution.Helpers do `20`. By passing a callback function to `dataloader/2` we can ensure that the value will fall nicely between 0 and 20. + If you have defined a + [custom batch query](https://hexdocs.pm/dataloader/Dataloader.Ecto.html#module-custom-batch-queries), + you can instead return a `Map` with `:batch` key to specify the `batch_key` of `Dataloader.load/4` / + `Dataloader.get/4` and an `:item` key to specify `item_key` argument of `Dataloader.get/4`. + + ``` + # ... + resolve dataloader(Posts, fn user, _args, _info -> + %{batch: {{:one, Post}, %{}}, item: [post_count: user]} + end) + # ... + ``` + ## Options - `:args` default: `%{}`. Any arguments you want to always pass into the From 1dd89cefac7b2bf0b62ea5101bf1510d7d7911ea Mon Sep 17 00:00:00 2001 From: Rosa Richter Date: Fri, 17 Sep 2021 12:52:45 -0600 Subject: [PATCH 002/120] Document Absinthe.Schema.Notation.import_sdl/1 This isn't much, but it's enough to figure out how to use external SDL files. Fixes #1092 --- lib/absinthe/schema/notation.ex | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/absinthe/schema/notation.ex b/lib/absinthe/schema/notation.ex index 102058a9a8..1454c4f7fa 100644 --- a/lib/absinthe/schema/notation.ex +++ b/lib/absinthe/schema/notation.ex @@ -1259,7 +1259,8 @@ defmodule Absinthe.Schema.Notation do @doc """ Import types defined using the Schema Definition Language (SDL). - TODO: Explain handlers + To add resolvers and middleware to the schema, use the callbacks defined in + `Absinthe.Schema`, like `c:Absinthe.Schema.hydrate/2`. ## Placement From 9cf9cf8bdf6c9b9a7a278a042303f4a9df781721 Mon Sep 17 00:00:00 2001 From: Sergei Iarkin Date: Mon, 1 Nov 2021 23:01:55 -0400 Subject: [PATCH 003/120] Update typespec for resolver_t As per code [of `Absinthe.Resolution.call/2`](https://github.com/absinthe-graphql/absinthe/blob/master/lib/absinthe/resolution.ex#L204-L212) three different forms of resolution function are supported. --- lib/absinthe/type/field.ex | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/lib/absinthe/type/field.ex b/lib/absinthe/type/field.ex index 9bfca934a1..fb44412c10 100644 --- a/lib/absinthe/type/field.ex +++ b/lib/absinthe/type/field.ex @@ -19,7 +19,13 @@ defmodule Absinthe.Type.Field do See the `Absinthe.Type.Field.t` explanation of `:resolve` for more information. """ - @type resolver_t :: (%{atom => any}, Absinthe.Resolution.t() -> result) + @type resolver_t :: + (Absinthe.Resolution.arguments(), Absinthe.Resolution.t() -> result) + | (Absinthe.Resolution.source(), + Absinthe.Resolution.arguments(), + Absinthe.Resolution.t() -> + result) + | {module(), atom()} @typedoc """ The result of a resolver. From 5bd9587df78ab8330a8706e7deda1b72cb325177 Mon Sep 17 00:00:00 2001 From: Maarten van Vliet Date: Wed, 19 Jan 2022 13:24:03 +0100 Subject: [PATCH 004/120] Remove unused Absinthe.Phase.Validation module --- .../document/validation/known_directives.ex | 1 - .../validation/known_fragment_names.ex | 1 - .../validation/lone_anonymous_operation.ex | 1 - .../validation/no_undefined_variables.ex | 1 - .../validation/no_unused_fragments.ex | 1 - .../validation/no_unused_variables.ex | 1 - .../validation/only_one_subscription.ex | 1 - .../validation/provided_an_operation.ex | 1 - .../validation/provided_non_null_variables.ex | 1 - .../validation/repeatable_directives.ex | 1 - .../phase/document/validation/scalar_leafs.ex | 1 - .../validation/selected_current_operation.ex | 1 - .../validation/unique_argument_names.ex | 1 - .../validation/unique_fragment_names.ex | 1 - .../validation/unique_input_field_names.ex | 1 - .../validation/unique_operation_names.ex | 1 - .../validation/unique_variable_names.ex | 1 - .../validation/variables_are_input_types.ex | 1 - .../schema/validation/known_directives.ex | 1 - lib/absinthe/phase/validation.ex | 26 ------------------- .../phase/validation/known_type_names.ex | 1 - 21 files changed, 46 deletions(-) delete mode 100644 lib/absinthe/phase/validation.ex diff --git a/lib/absinthe/phase/document/validation/known_directives.ex b/lib/absinthe/phase/document/validation/known_directives.ex index f1934afc6b..e1d978ee7b 100644 --- a/lib/absinthe/phase/document/validation/known_directives.ex +++ b/lib/absinthe/phase/document/validation/known_directives.ex @@ -4,7 +4,6 @@ defmodule Absinthe.Phase.Document.Validation.KnownDirectives do alias Absinthe.{Blueprint, Phase} use Absinthe.Phase - use Absinthe.Phase.Validation @doc """ Run the validation. diff --git a/lib/absinthe/phase/document/validation/known_fragment_names.ex b/lib/absinthe/phase/document/validation/known_fragment_names.ex index 4e8dbdbfc8..877fb98f63 100644 --- a/lib/absinthe/phase/document/validation/known_fragment_names.ex +++ b/lib/absinthe/phase/document/validation/known_fragment_names.ex @@ -7,7 +7,6 @@ defmodule Absinthe.Phase.Document.Validation.KnownFragmentNames do alias Absinthe.{Blueprint, Phase} use Absinthe.Phase - use Absinthe.Phase.Validation @doc """ Run the validation. diff --git a/lib/absinthe/phase/document/validation/lone_anonymous_operation.ex b/lib/absinthe/phase/document/validation/lone_anonymous_operation.ex index 857bf4a238..652152bdb1 100644 --- a/lib/absinthe/phase/document/validation/lone_anonymous_operation.ex +++ b/lib/absinthe/phase/document/validation/lone_anonymous_operation.ex @@ -7,7 +7,6 @@ defmodule Absinthe.Phase.Document.Validation.LoneAnonymousOperation do alias Absinthe.{Blueprint, Phase} use Absinthe.Phase - use Absinthe.Phase.Validation @doc """ Run the validation. diff --git a/lib/absinthe/phase/document/validation/no_undefined_variables.ex b/lib/absinthe/phase/document/validation/no_undefined_variables.ex index 87a8c8e1d8..582871a372 100644 --- a/lib/absinthe/phase/document/validation/no_undefined_variables.ex +++ b/lib/absinthe/phase/document/validation/no_undefined_variables.ex @@ -7,7 +7,6 @@ defmodule Absinthe.Phase.Document.Validation.NoUndefinedVariables do alias Absinthe.{Blueprint, Phase} use Absinthe.Phase - use Absinthe.Phase.Validation @doc """ Run the validation. diff --git a/lib/absinthe/phase/document/validation/no_unused_fragments.ex b/lib/absinthe/phase/document/validation/no_unused_fragments.ex index b5a3a2a89e..e2688bf31d 100644 --- a/lib/absinthe/phase/document/validation/no_unused_fragments.ex +++ b/lib/absinthe/phase/document/validation/no_unused_fragments.ex @@ -6,7 +6,6 @@ defmodule Absinthe.Phase.Document.Validation.NoUnusedFragments do alias Absinthe.{Blueprint, Phase} use Absinthe.Phase - use Absinthe.Phase.Validation @doc """ Run the validation. diff --git a/lib/absinthe/phase/document/validation/no_unused_variables.ex b/lib/absinthe/phase/document/validation/no_unused_variables.ex index aaacf2b04b..16788ff434 100644 --- a/lib/absinthe/phase/document/validation/no_unused_variables.ex +++ b/lib/absinthe/phase/document/validation/no_unused_variables.ex @@ -7,7 +7,6 @@ defmodule Absinthe.Phase.Document.Validation.NoUnusedVariables do alias Absinthe.{Blueprint, Phase} use Absinthe.Phase - use Absinthe.Phase.Validation @doc """ Run the validation. diff --git a/lib/absinthe/phase/document/validation/only_one_subscription.ex b/lib/absinthe/phase/document/validation/only_one_subscription.ex index 9a74fb705e..493a541a98 100644 --- a/lib/absinthe/phase/document/validation/only_one_subscription.ex +++ b/lib/absinthe/phase/document/validation/only_one_subscription.ex @@ -7,7 +7,6 @@ defmodule Absinthe.Phase.Document.Validation.OnlyOneSubscription do alias Absinthe.{Blueprint, Phase} use Absinthe.Phase - use Absinthe.Phase.Validation @doc """ Run the validation. diff --git a/lib/absinthe/phase/document/validation/provided_an_operation.ex b/lib/absinthe/phase/document/validation/provided_an_operation.ex index 26c1bd2ffc..869acb86ea 100644 --- a/lib/absinthe/phase/document/validation/provided_an_operation.ex +++ b/lib/absinthe/phase/document/validation/provided_an_operation.ex @@ -6,7 +6,6 @@ defmodule Absinthe.Phase.Document.Validation.ProvidedAnOperation do alias Absinthe.{Blueprint, Phase} use Absinthe.Phase - use Absinthe.Phase.Validation @doc """ Run the validation. diff --git a/lib/absinthe/phase/document/validation/provided_non_null_variables.ex b/lib/absinthe/phase/document/validation/provided_non_null_variables.ex index efe69c4bc7..286ddb910a 100644 --- a/lib/absinthe/phase/document/validation/provided_non_null_variables.ex +++ b/lib/absinthe/phase/document/validation/provided_non_null_variables.ex @@ -7,7 +7,6 @@ defmodule Absinthe.Phase.Document.Validation.ProvidedNonNullVariables do alias Absinthe.{Blueprint, Phase, Schema} use Absinthe.Phase - use Absinthe.Phase.Validation @doc """ Run the validation. diff --git a/lib/absinthe/phase/document/validation/repeatable_directives.ex b/lib/absinthe/phase/document/validation/repeatable_directives.ex index d84288333e..4391942904 100644 --- a/lib/absinthe/phase/document/validation/repeatable_directives.ex +++ b/lib/absinthe/phase/document/validation/repeatable_directives.ex @@ -4,7 +4,6 @@ defmodule Absinthe.Phase.Document.Validation.RepeatableDirectives do alias Absinthe.{Blueprint, Phase} use Absinthe.Phase - use Absinthe.Phase.Validation @doc """ Run the validation. diff --git a/lib/absinthe/phase/document/validation/scalar_leafs.ex b/lib/absinthe/phase/document/validation/scalar_leafs.ex index bb32fcfac0..30f55c50c7 100644 --- a/lib/absinthe/phase/document/validation/scalar_leafs.ex +++ b/lib/absinthe/phase/document/validation/scalar_leafs.ex @@ -38,7 +38,6 @@ defmodule Absinthe.Phase.Document.Validation.ScalarLeafs do alias Absinthe.{Blueprint, Phase, Type} use Absinthe.Phase - use Absinthe.Phase.Validation @doc """ Run the validation. diff --git a/lib/absinthe/phase/document/validation/selected_current_operation.ex b/lib/absinthe/phase/document/validation/selected_current_operation.ex index 29c83a03f2..89f63ea6c6 100644 --- a/lib/absinthe/phase/document/validation/selected_current_operation.ex +++ b/lib/absinthe/phase/document/validation/selected_current_operation.ex @@ -6,7 +6,6 @@ defmodule Absinthe.Phase.Document.Validation.SelectedCurrentOperation do alias Absinthe.{Blueprint, Phase} use Absinthe.Phase - use Absinthe.Phase.Validation @doc """ Run the validation. diff --git a/lib/absinthe/phase/document/validation/unique_argument_names.ex b/lib/absinthe/phase/document/validation/unique_argument_names.ex index a899b4ff25..ac836c8527 100644 --- a/lib/absinthe/phase/document/validation/unique_argument_names.ex +++ b/lib/absinthe/phase/document/validation/unique_argument_names.ex @@ -7,7 +7,6 @@ defmodule Absinthe.Phase.Document.Validation.UniqueArgumentNames do alias Absinthe.{Blueprint, Phase} use Absinthe.Phase - use Absinthe.Phase.Validation @doc """ Run the validation. diff --git a/lib/absinthe/phase/document/validation/unique_fragment_names.ex b/lib/absinthe/phase/document/validation/unique_fragment_names.ex index 0ec756b3e5..0b706a4cc5 100644 --- a/lib/absinthe/phase/document/validation/unique_fragment_names.ex +++ b/lib/absinthe/phase/document/validation/unique_fragment_names.ex @@ -6,7 +6,6 @@ defmodule Absinthe.Phase.Document.Validation.UniqueFragmentNames do alias Absinthe.{Blueprint, Phase} use Absinthe.Phase - use Absinthe.Phase.Validation @doc """ Run the validation. diff --git a/lib/absinthe/phase/document/validation/unique_input_field_names.ex b/lib/absinthe/phase/document/validation/unique_input_field_names.ex index 758f0b2df7..9d6354f50a 100644 --- a/lib/absinthe/phase/document/validation/unique_input_field_names.ex +++ b/lib/absinthe/phase/document/validation/unique_input_field_names.ex @@ -6,7 +6,6 @@ defmodule Absinthe.Phase.Document.Validation.UniqueInputFieldNames do alias Absinthe.{Blueprint, Phase} use Absinthe.Phase - use Absinthe.Phase.Validation @doc """ Run the validation. diff --git a/lib/absinthe/phase/document/validation/unique_operation_names.ex b/lib/absinthe/phase/document/validation/unique_operation_names.ex index c3f45faafd..31b9de8e68 100644 --- a/lib/absinthe/phase/document/validation/unique_operation_names.ex +++ b/lib/absinthe/phase/document/validation/unique_operation_names.ex @@ -6,7 +6,6 @@ defmodule Absinthe.Phase.Document.Validation.UniqueOperationNames do alias Absinthe.{Blueprint, Phase} use Absinthe.Phase - use Absinthe.Phase.Validation @doc """ Run the validation. diff --git a/lib/absinthe/phase/document/validation/unique_variable_names.ex b/lib/absinthe/phase/document/validation/unique_variable_names.ex index c00064acec..79c9d47156 100644 --- a/lib/absinthe/phase/document/validation/unique_variable_names.ex +++ b/lib/absinthe/phase/document/validation/unique_variable_names.ex @@ -7,7 +7,6 @@ defmodule Absinthe.Phase.Document.Validation.UniqueVariableNames do alias Absinthe.{Blueprint, Phase} use Absinthe.Phase - use Absinthe.Phase.Validation @doc """ Run the validation. diff --git a/lib/absinthe/phase/document/validation/variables_are_input_types.ex b/lib/absinthe/phase/document/validation/variables_are_input_types.ex index 66d97923c0..43e37dfa07 100644 --- a/lib/absinthe/phase/document/validation/variables_are_input_types.ex +++ b/lib/absinthe/phase/document/validation/variables_are_input_types.ex @@ -7,7 +7,6 @@ defmodule Absinthe.Phase.Document.Validation.VariablesAreInputTypes do alias Absinthe.{Blueprint, Phase, Schema, Type} use Absinthe.Phase - use Absinthe.Phase.Validation @doc """ Run the validation. diff --git a/lib/absinthe/phase/schema/validation/known_directives.ex b/lib/absinthe/phase/schema/validation/known_directives.ex index 23a0bb2d5a..7b80669b08 100644 --- a/lib/absinthe/phase/schema/validation/known_directives.ex +++ b/lib/absinthe/phase/schema/validation/known_directives.ex @@ -4,7 +4,6 @@ defmodule Absinthe.Phase.Schema.Validation.KnownDirectives do alias Absinthe.{Blueprint, Phase} use Absinthe.Phase - use Absinthe.Phase.Validation @doc """ Run the validation. diff --git a/lib/absinthe/phase/validation.ex b/lib/absinthe/phase/validation.ex deleted file mode 100644 index 0ee7629cb2..0000000000 --- a/lib/absinthe/phase/validation.ex +++ /dev/null @@ -1,26 +0,0 @@ -defmodule Absinthe.Phase.Validation do - @moduledoc false - - alias Absinthe.Blueprint - - defmacro __using__(_) do - quote do - import unquote(__MODULE__).Helpers - end - end - - defmodule Helpers do - @moduledoc false - - @spec any_invalid?([Blueprint.node_t()]) :: boolean - def any_invalid?(nodes) do - Enum.any?(nodes, fn - %{flags: %{invalid: _}} -> - true - - _ -> - false - end) - end - end -end diff --git a/lib/absinthe/phase/validation/known_type_names.ex b/lib/absinthe/phase/validation/known_type_names.ex index 7e6efd9b6b..bd46972826 100644 --- a/lib/absinthe/phase/validation/known_type_names.ex +++ b/lib/absinthe/phase/validation/known_type_names.ex @@ -15,7 +15,6 @@ defmodule Absinthe.Phase.Validation.KnownTypeNames do alias Absinthe.Phase.Document.Validation.Utils use Absinthe.Phase - use Absinthe.Phase.Validation @doc """ Run the validation. From 94762dcdf5121773310f73375c184b0517bc91ca Mon Sep 17 00:00:00 2001 From: Maarten van Vliet Date: Wed, 19 Jan 2022 13:24:19 +0100 Subject: [PATCH 005/120] Remove unused Absinthe.Phase.Schema.Validation module --- lib/absinthe/phase/schema/validation.ex | 11 ----------- 1 file changed, 11 deletions(-) delete mode 100644 lib/absinthe/phase/schema/validation.ex diff --git a/lib/absinthe/phase/schema/validation.ex b/lib/absinthe/phase/schema/validation.ex deleted file mode 100644 index 053b25e670..0000000000 --- a/lib/absinthe/phase/schema/validation.ex +++ /dev/null @@ -1,11 +0,0 @@ -defmodule Absinthe.Phase.Schema.Validation do - @moduledoc false - - alias Absinthe.Phase - - def pipeline do - [ - Phase.Validation.KnownDirectives - ] - end -end From 648c4bf828136c435e7ede6512245a827991fd14 Mon Sep 17 00:00:00 2001 From: Maarten van Vliet Date: Wed, 19 Jan 2022 13:24:50 +0100 Subject: [PATCH 006/120] Remove unused Type.Fetch module --- lib/absinthe/type/argument.ex | 2 -- lib/absinthe/type/fetch.ex | 15 --------------- lib/absinthe/type/field.ex | 2 -- lib/absinthe/type/input_object.ex | 1 - lib/absinthe/type/list.ex | 1 - lib/absinthe/type/non_null.ex | 1 - test/absinthe/schema/manipulation_test.exs | 2 +- 7 files changed, 1 insertion(+), 23 deletions(-) delete mode 100644 lib/absinthe/type/fetch.ex diff --git a/lib/absinthe/type/argument.ex b/lib/absinthe/type/argument.ex index 3244f2597a..2edafa5ff2 100644 --- a/lib/absinthe/type/argument.ex +++ b/lib/absinthe/type/argument.ex @@ -7,8 +7,6 @@ defmodule Absinthe.Type.Argument do alias Absinthe.Type - use Type.Fetch - @typedoc """ Argument configuration diff --git a/lib/absinthe/type/fetch.ex b/lib/absinthe/type/fetch.ex deleted file mode 100644 index f4149a3ba8..0000000000 --- a/lib/absinthe/type/fetch.ex +++ /dev/null @@ -1,15 +0,0 @@ -defmodule Absinthe.Type.Fetch do - @moduledoc false - - defmacro __using__(_) do - quote do - def fetch(container, key) do - if Map.has_key?(container, key) do - {:ok, container |> Map.get(key)} - else - :error - end - end - end - end -end diff --git a/lib/absinthe/type/field.ex b/lib/absinthe/type/field.ex index 9bfca934a1..fde1ee5ae8 100644 --- a/lib/absinthe/type/field.ex +++ b/lib/absinthe/type/field.ex @@ -12,8 +12,6 @@ defmodule Absinthe.Type.Field do alias Absinthe.Type alias Absinthe.Type.Deprecation - use Type.Fetch - @typedoc """ A resolver function. diff --git a/lib/absinthe/type/input_object.ex b/lib/absinthe/type/input_object.ex index 64fa4b2bcb..eb9d42d35c 100644 --- a/lib/absinthe/type/input_object.ex +++ b/lib/absinthe/type/input_object.ex @@ -32,7 +32,6 @@ defmodule Absinthe.Type.InputObject do """ use Absinthe.Introspection.TypeKind, :input_object - use Absinthe.Type.Fetch alias Absinthe.Type diff --git a/lib/absinthe/type/list.ex b/lib/absinthe/type/list.ex index ccf9ff13da..ba7633f6ec 100644 --- a/lib/absinthe/type/list.ex +++ b/lib/absinthe/type/list.ex @@ -19,7 +19,6 @@ defmodule Absinthe.Type.List do """ use Absinthe.Introspection.TypeKind, :list - use Absinthe.Type.Fetch @typedoc " A defined list type. diff --git a/lib/absinthe/type/non_null.ex b/lib/absinthe/type/non_null.ex index 5e3d6c40b3..2c358ada73 100644 --- a/lib/absinthe/type/non_null.ex +++ b/lib/absinthe/type/non_null.ex @@ -22,7 +22,6 @@ defmodule Absinthe.Type.NonNull do """ use Absinthe.Introspection.TypeKind, :non_null - use Absinthe.Type.Fetch @typedoc """ A defined non-null type. diff --git a/test/absinthe/schema/manipulation_test.exs b/test/absinthe/schema/manipulation_test.exs index 674086a2f5..5cfd5feee3 100644 --- a/test/absinthe/schema/manipulation_test.exs +++ b/test/absinthe/schema/manipulation_test.exs @@ -39,7 +39,7 @@ defmodule Absinthe.Schema.ManipulationTest do %{ source: source } -> - private = source[:__private__] || [] + private = source.__private__ || [] meta_items = private[:meta] || [] {:ok, meta_items[:some_string_meta]} From 5ca9fff24a5263a07c92a07f3206356508a2e251 Mon Sep 17 00:00:00 2001 From: Maarten van Vliet Date: Wed, 19 Jan 2022 13:25:10 +0100 Subject: [PATCH 007/120] Remove unused Absinthe.Type.BuiltIns.Scalars.Utils macro module --- lib/absinthe/type/built_ins/scalars/utils.ex | 20 -------------------- 1 file changed, 20 deletions(-) delete mode 100644 lib/absinthe/type/built_ins/scalars/utils.ex diff --git a/lib/absinthe/type/built_ins/scalars/utils.ex b/lib/absinthe/type/built_ins/scalars/utils.ex deleted file mode 100644 index b251b57f7b..0000000000 --- a/lib/absinthe/type/built_ins/scalars/utils.ex +++ /dev/null @@ -1,20 +0,0 @@ -defmodule Absinthe.Type.BuiltIns.Scalars.Utils do - @moduledoc false - - # Parse, supporting pulling values out of AST nodes - defmacro parse_with(node_types, coercion) do - quote do - fn - %{value: value} = node -> - if Enum.member?(unquote(node_types), node) do - unquote(coercion).(value) - else - nil - end - - other -> - unquote(coercion).(other) - end - end - end -end From 449d5e0eec5305d53c3dd521d4af4437e7476f63 Mon Sep 17 00:00:00 2001 From: Paulo Daniel Gonzalez Date: Fri, 21 Jan 2022 18:15:25 -0600 Subject: [PATCH 008/120] Add example of enum usage with field, per Slack discussion --- lib/absinthe/schema/notation.ex | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/lib/absinthe/schema/notation.ex b/lib/absinthe/schema/notation.ex index cf61b25605..1a84a7e579 100644 --- a/lib/absinthe/schema/notation.ex +++ b/lib/absinthe/schema/notation.ex @@ -14,12 +14,17 @@ defmodule Absinthe.Schema.Notation do object :item do field :id, :id field :name, :string + field :status, :status_enum + end + + enum :status_enum do + value :current + value :discontinued end # ... end - """ Module.register_attribute(__MODULE__, :placement, accumulate: true) From 0482fe3c8ef763906068e97994d01c9466408566 Mon Sep 17 00:00:00 2001 From: Maarten van Vliet Date: Fri, 26 Aug 2022 18:36:07 +0200 Subject: [PATCH 009/120] Test on otp 25 --- .github/workflows/elixir.yml | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/.github/workflows/elixir.yml b/.github/workflows/elixir.yml index c79946a104..9e54243ebc 100644 --- a/.github/workflows/elixir.yml +++ b/.github/workflows/elixir.yml @@ -14,20 +14,25 @@ jobs: strategy: matrix: elixir: - - "1.10" - "1.11" - "1.12" - "1.13" + - "1.14" otp: - "22" - "23" - "24" + - "25" exclude: - - elixir: "1.10" - otp: "24" + - elixir: "1.11" + otp: "25" + - elixir: "1.12" + otp: "25" + - elixir: "1.14" + otp: "22" include: - - elixir: "1.13" - otp: "24" + - elixir: "1.14" + otp: "25" format: true steps: From f64dd3866162b316b41258c6558290e57ff2f601 Mon Sep 17 00:00:00 2001 From: Maarten van Vliet Date: Wed, 14 Sep 2022 12:07:17 +0200 Subject: [PATCH 010/120] Fix SDL type definition rendering order In OTP-25 the SDL rendering was different compared to earlier versions. I traced it down to `:digraph_utils.topsort()` although could not find a relevant change in OTP-25 that would explain the difference. `topsort/1` is used to sort the nodes in the graph so that the import process can be done in the correct order in the FieldImports phase. I've changed it to no longer rely on `topsort/1`. Since the `NoCircularFieldImports` ensures there are no cycles in the graph we can use recursion for solving the imports without ending up in an endless loop. This could be less efficient. The `topsort/1` ensured that the order of imports was such that when `A` imported `B`, `B` was already imported, etc up the hierachy chain. If `C` also imported `B`, the imports for `B` would also already be done. So, `import_fields(B)` would be called once for the entire graph. Using recursion, `import_fields(B)` would be called twice, once for `A` and once for `C`. If B itself imported other fields, those would also be called multiple times. I don't think this will be a problem in practice. The cost is only incurred for schema building, and the `import_fields` graphs are probably pretty shallow. The rendering order now matches the order the type definitions were defined in. --- lib/absinthe/phase/schema/field_imports.ex | 20 ++++---- .../validation/no_circular_field_imports.ex | 34 ++++---------- test/absinthe/schema/notation/import_test.exs | 8 ++-- test/absinthe/schema/sdl_render_test.exs | 24 +++++----- .../schema/type_system_directive_test.exs | 46 +++++++++---------- test/absinthe/schema_test.exs | 21 +++++++-- 6 files changed, 76 insertions(+), 77 deletions(-) diff --git a/lib/absinthe/phase/schema/field_imports.ex b/lib/absinthe/phase/schema/field_imports.ex index 55fd5d9fbd..69c0f1c7a9 100644 --- a/lib/absinthe/phase/schema/field_imports.ex +++ b/lib/absinthe/phase/schema/field_imports.ex @@ -11,15 +11,14 @@ defmodule Absinthe.Phase.Schema.FieldImports do end def handle_imports(%Schema.SchemaDefinition{} = schema) do - # Per Phase.Schema.ValidateTypeReferences, the types are already - # in the order they need to be in to accumulate imports properly. - types = - Enum.reduce(schema.type_definitions, %{}, fn type, types -> - Map.put(types, type.identifier, import_fields(type, types)) + types = Map.new(schema.type_definitions, &{&1.identifier, &1}) + + type_definitions = + Enum.map(schema.type_definitions, fn type -> + import_fields(type, types) end) - types = Enum.map(schema.type_definitions, &Map.fetch!(types, &1.identifier)) - {:halt, %{schema | type_definitions: types}} + {:halt, %{schema | type_definitions: type_definitions}} end def handle_imports(node), do: node @@ -32,9 +31,14 @@ defmodule Absinthe.Phase.Schema.FieldImports do @exclude_fields [ :__typename ] + # Per Absinthe.Phase.Schema.Validation.NoCircularFieldImports, there are no cycles + # in the field imports. Therefore we can use recursion to resolve the imports. def import_fields(%def_type{} = type, types) when def_type in @can_import do Enum.reduce(type.imports, type, fn {source, opts}, type -> - source_type = Map.fetch!(types, source) + source_type = + types + |> Map.fetch!(source) + |> import_fields(types) rejections = Keyword.get(opts, :except, []) ++ @exclude_fields diff --git a/lib/absinthe/phase/schema/validation/no_circular_field_imports.ex b/lib/absinthe/phase/schema/validation/no_circular_field_imports.ex index 1c30669bfb..bbef70bf85 100644 --- a/lib/absinthe/phase/schema/validation/no_circular_field_imports.ex +++ b/lib/absinthe/phase/schema/validation/no_circular_field_imports.ex @@ -11,40 +11,24 @@ defmodule Absinthe.Phase.Schema.Validation.NoCircularFieldImports do end def validate_schema(%Schema.SchemaDefinition{type_definitions: types} = schema) do - {:halt, %{schema | type_definitions: sort_and_validate_types(types)}} + {:halt, %{schema | type_definitions: validate_cycles(types)}} end def validate_schema(node), do: node - def sort_and_validate_types(types) do + def validate_cycles(types) do graph = :digraph.new([:cyclic]) try do _ = build_import_graph(types, graph) - {types, cycles?} = - Enum.reduce(types, {%{}, false}, fn type, {types, cycles?} -> - if cycle = :digraph.get_cycle(graph, type.identifier) do - type = type |> put_error(error(type, cycle)) - {Map.put(types, type.identifier, type), true} - else - {Map.put(types, type.identifier, type), cycles?} - end - end) - - if cycles? do - Map.values(types) - else - graph - |> :digraph_utils.topsort() - |> Enum.reverse() - |> Enum.flat_map(fn identifier -> - case Map.fetch(types, identifier) do - {:ok, type} -> [type] - _ -> [] - end - end) - end + Enum.map(types, fn type -> + if cycle = :digraph.get_cycle(graph, type.identifier) do + type |> put_error(error(type, cycle)) + else + type + end + end) after :digraph.delete(graph) end diff --git a/test/absinthe/schema/notation/import_test.exs b/test/absinthe/schema/notation/import_test.exs index 8699572e0d..fc78b7ca44 100644 --- a/test/absinthe/schema/notation/import_test.exs +++ b/test/absinthe/schema/notation/import_test.exs @@ -159,26 +159,26 @@ defmodule Absinthe.Schema.Notation.ImportTest do assert {:error, [ %Absinthe.Phase.Error{ - extra: :bar, + extra: :foo, locations: [ %{ line: _ } ], message: - "Field Import Cycle Error\n\nField Import in object `bar' `import_fields([foo: []]) forms a cycle via: ([:bar, :foo, :bar])", + "Field Import Cycle Error\n\nField Import in object `foo' `import_fields([bar: []]) forms a cycle via: ([:foo, :bar, :foo])", path: [], phase: Absinthe.Phase.Schema.Validation.NoCircularFieldImports }, %Absinthe.Phase.Error{ - extra: :foo, + extra: :bar, locations: [ %{ line: _ } ], message: - "Field Import Cycle Error\n\nField Import in object `foo' `import_fields([bar: []]) forms a cycle via: ([:foo, :bar, :foo])", + "Field Import Cycle Error\n\nField Import in object `bar' `import_fields([foo: []]) forms a cycle via: ([:bar, :foo, :bar])", path: [], phase: Absinthe.Phase.Schema.Validation.NoCircularFieldImports } diff --git a/test/absinthe/schema/sdl_render_test.exs b/test/absinthe/schema/sdl_render_test.exs index a574136084..7adc2f5ba2 100644 --- a/test/absinthe/schema/sdl_render_test.exs +++ b/test/absinthe/schema/sdl_render_test.exs @@ -246,10 +246,10 @@ defmodule Absinthe.Schema.SdlRenderTest do directive @foo(baz: String) on FIELD - "Escaped\\t\\\"descrição\\/description\\\"" + \"Escaped\\t\\\"descrição\\/description\\\"\" type RootQueryType { echo( - "The number of times" + \"The number of times\" times: Int timeInterval: Int @@ -257,24 +257,18 @@ defmodule Absinthe.Schema.SdlRenderTest do search: SearchResult } - type Category { - name: String + enum OrderStatus { + DELIVERED + PROCESSING + PICKING } - union SearchResult = Order | Category - enum Status { ONE TWO THREE } - enum OrderStatus { - DELIVERED - PROCESSING - PICKING - } - type Order { imported: Boolean! id: ID @@ -282,6 +276,12 @@ defmodule Absinthe.Schema.SdlRenderTest do status: OrderStatus otherStatus: Status } + + type Category { + name: String + } + + union SearchResult = Order | Category """ end end diff --git a/test/absinthe/schema/type_system_directive_test.exs b/test/absinthe/schema/type_system_directive_test.exs index be233a9521..6034d7bbff 100644 --- a/test/absinthe/schema/type_system_directive_test.exs +++ b/test/absinthe/schema/type_system_directive_test.exs @@ -184,33 +184,35 @@ defmodule Absinthe.Schema.TypeSystemDirectiveTest do end @macro_schema_sdl """ - schema @feature(name: ":schema") { + schema @feature(name: \":schema\") { query: RootQueryType } - interface Animal @feature(name: ":interface") { - legCount: Int! @feature(name: \"\"\" - Multiline here? - Second line - \"\"\") + type RootQueryType { + post: Post @feature(name: \":field_definition\") + sweet: SweetScalar + which: Category + pet: Dog + search(filter: SearchFilter @feature(name: \":argument_definition\")): SearchResult @feature(name: \":argument_definition\") } - input SearchFilter @feature(name: ":input_object") { - query: String @feature(name: ":input_field_definition") + type Post @feature(name: \":object\", number: 3) { + name: String @deprecated(reason: \"Bye\") } - type Post @feature(name: ":object", number: 3) { - name: String @deprecated(reason: "Bye") - } + scalar SweetScalar @feature(name: \":scalar\") - scalar SweetScalar @feature(name: ":scalar") + enum Category @feature(name: \":enum\") { + THIS + THAT @feature(name: \":enum_value\") + THE_OTHER @deprecated(reason: \"It's old\") + } - type RootQueryType { - post: Post @feature(name: ":field_definition") - sweet: SweetScalar - which: Category - pet: Dog - search(filter: SearchFilter @feature(name: ":argument_definition")): SearchResult @feature(name: ":argument_definition") + interface Animal @feature(name: \":interface\") { + legCount: Int! @feature(name: \"\"\" + Multiline here? + Second line + \"\"\") } type Dog implements Animal { @@ -218,13 +220,11 @@ defmodule Absinthe.Schema.TypeSystemDirectiveTest do name: String! @external } - enum Category @feature(name: ":enum") { - THIS - THAT @feature(name: ":enum_value") - THE_OTHER @deprecated(reason: "It's old") + input SearchFilter @feature(name: \":input_object\") { + query: String @feature(name: \":input_field_definition\") } - union SearchResult @feature(name: ":union") = Dog | Post + union SearchResult @feature(name: \":union\") = Dog | Post """ describe "with macro schema" do test "Render SDL with Type System Directives applied" do diff --git a/test/absinthe/schema_test.exs b/test/absinthe/schema_test.exs index 38da3b75fc..aad0801b6a 100644 --- a/test/absinthe/schema_test.exs +++ b/test/absinthe/schema_test.exs @@ -324,13 +324,13 @@ defmodule Absinthe.SchemaTest do query: RootQueryType } - type RootSubscriptionTypeThing { - name: String - } - type RootQueryType { name(familyName: Boolean): String } + + type RootSubscriptionTypeThing { + name: String + } """ == Schema.to_sdl(RootsSchemaDeclaration) end end @@ -356,7 +356,18 @@ defmodule Absinthe.SchemaTest do describe "to_sdl/1" do test "return schema sdl" do assert Schema.to_sdl(SourceSchema) == """ - schema {\n query: RootQueryType\n}\n\ntype Foo {\n name: String\n}\n\n\"can describe query\"\ntype RootQueryType {\n foo: Foo\n} + schema { + query: RootQueryType + } + + \"can describe query\" + type RootQueryType { + foo: Foo + } + + type Foo { + name: String + } """ end end From 89fa88a080a410d4d8c8f4f43ff3924adaa48c79 Mon Sep 17 00:00:00 2001 From: Maarten van Vliet Date: Wed, 14 Sep 2022 13:56:33 +0200 Subject: [PATCH 011/120] Remove Elixir 1.10 support --- mix.exs | 2 +- test/mix/tasks/absinthe.schema.json_test.exs | 21 ++------------------ test/mix/tasks/absinthe.schema.sdl_test.exs | 21 ++------------------ 3 files changed, 5 insertions(+), 39 deletions(-) diff --git a/mix.exs b/mix.exs index b7f14bf10d..13ad408ebe 100644 --- a/mix.exs +++ b/mix.exs @@ -8,7 +8,7 @@ defmodule Absinthe.Mixfile do [ app: :absinthe, version: @version, - elixir: "~> 1.10", + elixir: "~> 1.11", elixirc_paths: elixirc_paths(Mix.env()), build_embedded: Mix.env() == :prod, start_permanent: Mix.env() == :prod, diff --git a/test/mix/tasks/absinthe.schema.json_test.exs b/test/mix/tasks/absinthe.schema.json_test.exs index acf0ea6110..d7081cbd42 100644 --- a/test/mix/tasks/absinthe.schema.json_test.exs +++ b/test/mix/tasks/absinthe.schema.json_test.exs @@ -95,12 +95,7 @@ defmodule Mix.Tasks.Absinthe.Schema.JsonTest do assert ugly_content == "test-encoder-ugly" end - if Version.compare(System.version(), "1.11.0") == :lt do - setup :tmp_dir_fallback - else - @tag :tmp_dir - end - + @tag :tmp_dir test "generates a JSON file", %{tmp_dir: tmp_dir} do path = Path.join(tmp_dir, "schema.json") @@ -110,12 +105,7 @@ defmodule Mix.Tasks.Absinthe.Schema.JsonTest do assert File.exists?(path) end - if Version.compare(System.version(), "1.11.0") == :lt do - setup :tmp_dir_fallback - else - @tag :tmp_dir - end - + @tag :tmp_dir test "generates a JSON file for a persistent term schema provider", %{tmp_dir: tmp_dir} do path = Path.join(tmp_dir, "schema.json") @@ -125,11 +115,4 @@ defmodule Mix.Tasks.Absinthe.Schema.JsonTest do assert File.exists?(path) end end - - defp tmp_dir_fallback(_) do - path = Path.join("tmp", "#{__MODULE__}") - File.mkdir_p!(path) - on_exit(fn -> File.rm_rf!(path) end) - [tmp_dir: path] - end end diff --git a/test/mix/tasks/absinthe.schema.sdl_test.exs b/test/mix/tasks/absinthe.schema.sdl_test.exs index 8da3afc4ac..ad21ab7571 100644 --- a/test/mix/tasks/absinthe.schema.sdl_test.exs +++ b/test/mix/tasks/absinthe.schema.sdl_test.exs @@ -174,12 +174,7 @@ defmodule Mix.Tasks.Absinthe.Schema.SdlTest do assert schema =~ "type Robot implements Being" end - if Version.compare(System.version(), "1.11.0") == :lt do - setup :tmp_dir_fallback - else - @tag :tmp_dir - end - + @tag :tmp_dir test "generates an SDL file", %{tmp_dir: tmp_dir} do path = Path.join(tmp_dir, "schema.sdl") @@ -189,12 +184,7 @@ defmodule Mix.Tasks.Absinthe.Schema.SdlTest do assert File.exists?(path) end - if Version.compare(System.version(), "1.11.0") == :lt do - setup :tmp_dir_fallback - else - @tag :tmp_dir - end - + @tag :tmp_dir test "generates an SDL file for a persistent term schema provider", %{tmp_dir: tmp_dir} do path = Path.join(tmp_dir, "schema.sdl") @@ -204,11 +194,4 @@ defmodule Mix.Tasks.Absinthe.Schema.SdlTest do assert File.exists?(path) end end - - defp tmp_dir_fallback(_) do - path = Path.join("tmp", "#{__MODULE__}") - File.mkdir_p!(path) - on_exit(fn -> File.rm_rf!(path) end) - [tmp_dir: path] - end end From bdf96dc48b5554ebc44b29c49474d611cb7bcf94 Mon Sep 17 00:00:00 2001 From: Maarten van Vliet Date: Mon, 3 Oct 2022 19:37:35 +0200 Subject: [PATCH 012/120] Ensure an object has unique fields See https://github.com/absinthe-graphql/absinthe/issues/1049 The issue was not resolved for field identifiers. This issue should resolve that. --- .../schema/validation/unique_field_names.ex | 17 +++++++++--- .../schema/rule/unique_field_names_test.exs | 26 +++++++++++++++++-- 2 files changed, 37 insertions(+), 6 deletions(-) diff --git a/lib/absinthe/phase/schema/validation/unique_field_names.ex b/lib/absinthe/phase/schema/validation/unique_field_names.ex index 40a9774520..9a2aa61c2e 100644 --- a/lib/absinthe/phase/schema/validation/unique_field_names.ex +++ b/lib/absinthe/phase/schema/validation/unique_field_names.ex @@ -8,6 +8,7 @@ defmodule Absinthe.Phase.Schema.Validation.UniqueFieldNames do bp = bp |> Blueprint.prewalk(&handle_schemas(&1, :name)) + |> Blueprint.prewalk(&handle_schemas(&1, :identifier)) {:ok, bp} end @@ -32,7 +33,7 @@ defmodule Absinthe.Phase.Schema.Validation.UniqueFieldNames do name_counts = Enum.frequencies_by(object.fields, &Map.get(&1, key)) if duplicate?(name_counts, field, key) do - Absinthe.Phase.put_error(field, error(field, object)) + Absinthe.Phase.put_error(field, error(field, object, key)) else field end @@ -50,20 +51,28 @@ defmodule Absinthe.Phase.Schema.Validation.UniqueFieldNames do Map.get(name_counts, field_identifier, 0) > 1 end - defp error(field, object) do + defp error(field, object, key) do %Absinthe.Phase.Error{ - message: explanation(field, object), + message: explanation(field, object, key), locations: [field.__reference__.location], phase: __MODULE__, extra: field } end - def explanation(field, object) do + def explanation(field, object, :name) do """ The field #{inspect(field.name)} is not unique in type #{inspect(object.name)}. The field must have a unique name within that Object type; no two fields may share the same name. """ end + + def explanation(field, object, :identifier) do + """ + The field identifier #{inspect(field.identifier)} is not unique in type #{inspect(object.name)}. + + The field must have a unique identifier within that Object type; no two fields may share the same identifier. + """ + end end diff --git a/test/absinthe/schema/rule/unique_field_names_test.exs b/test/absinthe/schema/rule/unique_field_names_test.exs index 8e4415ab77..a62329e53f 100644 --- a/test/absinthe/schema/rule/unique_field_names_test.exs +++ b/test/absinthe/schema/rule/unique_field_names_test.exs @@ -1,7 +1,21 @@ defmodule Absinthe.Schema.Rule.UniqueFieldNamesTest do use Absinthe.Case, async: true - @duplicate_object_fields ~S( + @duplicate_object_fields_macro ~S( + defmodule DuplicateObjectFields do + use Absinthe.Schema + + query do + end + + object :dog do + field :name, :string + field :name, :integer, name: "dogName" + end + end + ) + + @duplicate_object_fields_sdl ~S( defmodule DuplicateObjectFields do use Absinthe.Schema @@ -49,11 +63,19 @@ defmodule Absinthe.Schema.Rule.UniqueFieldNamesTest do end ) + test "errors on non unique object field identifier" do + error = ~r/The field identifier :name is not unique in type \"Dog\"./ + + assert_raise(Absinthe.Schema.Error, error, fn -> + Code.eval_string(@duplicate_object_fields_macro) + end) + end + test "errors on non unique object field names" do error = ~r/The field \"name\" is not unique in type \"Dog\"./ assert_raise(Absinthe.Schema.Error, error, fn -> - Code.eval_string(@duplicate_object_fields) + Code.eval_string(@duplicate_object_fields_sdl) end) end From b3b899dd078d859e5eece1fcbc1c5e5bff5cccb2 Mon Sep 17 00:00:00 2001 From: Maarten van Vliet Date: Mon, 3 Oct 2022 19:41:44 +0200 Subject: [PATCH 013/120] Update changelog --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e85eea8208..a226cbf679 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,7 +1,7 @@ # Changelog ## Unreleased - +- Bug Fix: [Validate field identifier uniqueness](https://github.com/absinthe-graphql/absinthe/pull/1200) - Bug Fix: [Validate type references for invalid wrapped types](https://github.com/absinthe-graphql/absinthe/pull/1195) - Breaking Bugfix: [Validate repeatable directives on schemas](https://github.com/absinthe-graphql/absinthe/pull/1179) - Bug Fix: Adds **optional fix** for non compliant built-in scalar Int type. `use Absinthe.Schema, use_spec_compliant_int_scalar: true` in your schema to use the fixed Int type. It is also advisable to upgrade for custom types if you are leveraging the use of integers outside the GraphQl standard. [#1131](https://github.com/absinthe-graphql/absinthe/pull/1131). From 85c5138ddf4f2d8410e8f5daa74c6ffa56052eac Mon Sep 17 00:00:00 2001 From: Francisco Marques Date: Mon, 10 Oct 2022 00:15:14 +0100 Subject: [PATCH 014/120] fix broken links --- guides/upgrading/v1.4.md | 2 +- lib/absinthe/type/built_ins/spec_compliant_int.ex | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/guides/upgrading/v1.4.md b/guides/upgrading/v1.4.md index c4385a2c53..b4539e9cec 100644 --- a/guides/upgrading/v1.4.md +++ b/guides/upgrading/v1.4.md @@ -97,7 +97,7 @@ The reason for this is that you can also access the `context` within the `exec` ## Calling All Resolvers: The Null Literal Has Arrived -Absinthe now supports [GraphQL `null` literals](http://facebook.github.io/graphql/October2016/#sec-Null-Value). +Absinthe now supports [GraphQL `null` literals](https://spec.graphql.org/October2016/#sec-Null-Value). `null` values, when provided as arguments, are passed on to Absinthe resolvers as `nil` (provided they don't run afoul of a `non_null/1` argument constraint). diff --git a/lib/absinthe/type/built_ins/spec_compliant_int.ex b/lib/absinthe/type/built_ins/spec_compliant_int.ex index 3c7d989add..8cb3c9e76d 100644 --- a/lib/absinthe/type/built_ins/spec_compliant_int.ex +++ b/lib/absinthe/type/built_ins/spec_compliant_int.ex @@ -7,7 +7,7 @@ defmodule Absinthe.Type.BuiltIns.SpecCompliantInt 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). + [GraphQl spec](https://spec.graphql.org/October2021/#sec-Int). """ serialize &__MODULE__.serialize_int/1 From e4d83b6e2280680558f992e51232a4dded777e9c Mon Sep 17 00:00:00 2001 From: Francisco Marques Date: Mon, 10 Oct 2022 00:19:54 +0100 Subject: [PATCH 015/120] upgraded http links to https --- CONTRIBUTING.md | 2 +- README.md | 4 ++-- guides/introduction/learning.md | 4 ++-- guides/introduction/overview.md | 2 +- guides/introspection.md | 2 +- guides/plug-phoenix.md | 2 +- guides/tutorial/our-first-query.md | 2 +- guides/tutorial/start.md | 2 +- lib/absinthe.ex | 2 +- lib/absinthe/type/scalar.ex | 2 +- test/support/fixtures/fake_definition.graphql | 2 +- 11 files changed, 13 insertions(+), 13 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 8d5de76cca..8d4a1cda07 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -39,4 +39,4 @@ Some things that will increase the chance that your pull request is accepted: Thanks again for helping! -[commit]: http://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html +[commit]: https://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html diff --git a/README.md b/README.md index 1877e8f404..e1c7b039bb 100644 --- a/README.md +++ b/README.md @@ -17,7 +17,7 @@ Goals: - Detailed error messages and documentation. - A focus on robustness and production-level performance. -Please see the website at [http://absinthe-graphql.org](http://absinthe-graphql.org). +Please see the website at [https://absinthe-graphql.org](https://absinthe-graphql.org). ## Why Use Absinthe? @@ -86,7 +86,7 @@ See [CHANGELOG](./CHANGELOG.md) for upgrade steps between versions. - [Absinthe hexdocs](https://hexdocs.pm/absinthe). - For the tutorial, guides, and general information about Absinthe-related - projects, see [http://absinthe-graphql.org](http://absinthe-graphql.org). + projects, see [https://absinthe-graphql.org](https://absinthe-graphql.org). ### Mix Tasks diff --git a/guides/introduction/learning.md b/guides/introduction/learning.md index 36a22ef2e2..0c05dd3527 100644 --- a/guides/introduction/learning.md +++ b/guides/introduction/learning.md @@ -8,7 +8,7 @@ The following are some Absinthe-specific educational resources that are availabl ## Online Resources -* [Website](http://absinthe-graphql.org) (mostly just links elsewhere) +* [Website](https://absinthe-graphql.org) (mostly just links elsewhere) * [Documentation](https://hexdocs.pm/absinthe) (current stable release) * [How to GraphQL (with Absinthe)](https://www.howtographql.com/graphql-elixir/0-introduction/) @@ -21,4 +21,4 @@ The following are some Absinthe-specific educational resources that are availabl There's a ton of GraphQL resources on the web. -The [official website](http://graphql.org/) and [How to GraphQL](https://www.howtographql.com) are good places to start. +The [official website](https://graphql.org/) and [How to GraphQL](https://www.howtographql.com) are good places to start. diff --git a/guides/introduction/overview.md b/guides/introduction/overview.md index 4ba2b514b9..b371012850 100644 --- a/guides/introduction/overview.md +++ b/guides/introduction/overview.md @@ -10,7 +10,7 @@ If you're new to GraphQL, we suggest you read up a bit on GraphQL's foundational Here are a few resources that might be helpful: -- The official [GraphQL](http://graphql.org/) website +- The official [GraphQL](https://graphql.org/) website - [How to GraphQL](https://www.howtographql.com/), which includes a [brief tutorial](https://www.howtographql.com/graphql-elixir/0-introduction/) using Absinthe ## Absinthe diff --git a/guides/introspection.md b/guides/introspection.md index 14c939ef05..a61c935946 100644 --- a/guides/introspection.md +++ b/guides/introspection.md @@ -121,7 +121,7 @@ end ``` If you'd prefer to use a desktop application, we recommend using the pre-built -[Electron](http://electron.atom.io)-based wrapper application, +[Electron](https://electron.atom.io)-based wrapper application, [GraphiQL.app](https://github.com/skevy/graphiql-app). ### GraphQL Hub diff --git a/guides/plug-phoenix.md b/guides/plug-phoenix.md index e17742df25..c9af231e19 100644 --- a/guides/plug-phoenix.md +++ b/guides/plug-phoenix.md @@ -100,7 +100,7 @@ With a query string: ?query=query+GetItem($id:ID!){item(id:$id){name}}&variables={id:"foo"} ``` -Due to [varying limits on the maximum size of URLs](http://stackoverflow.com/questions/417142/what-is-the-maximum-length-of-a-url-in-different-browsers), +Due to [varying limits on the maximum size of URLs](https://stackoverflow.com/questions/417142/what-is-the-maximum-length-of-a-url-in-different-browsers), we recommend using one of the POST options below instead, putting the `query` into the body of the request. ### Via an `application/json` POST diff --git a/guides/tutorial/our-first-query.md b/guides/tutorial/our-first-query.md index bad7cf3a2c..8cf676d5b1 100644 --- a/guides/tutorial/our-first-query.md +++ b/guides/tutorial/our-first-query.md @@ -110,7 +110,7 @@ which is where all the domain logic for posts lives, invoking its > for [advanced plugins](middleware-and-plugins.md) that further process the data. > > If you're asking yourself what the implementation of the domain logic looks like, and exactly how -> the related Ecto schemas are built, read through the code in the [absinthe_tutorial](http://github.com/absinthe-graphql/absinthe_tutorial) +> the related Ecto schemas are built, read through the code in the [absinthe_tutorial](https://github.com/absinthe-graphql/absinthe_tutorial) > repository. The tutorial content here is intentionally focused on the Absinthe-specific code. Now that we have the functional pieces in place, let's configure our diff --git a/guides/tutorial/start.md b/guides/tutorial/start.md index 54be0e9399..85e2964c37 100644 --- a/guides/tutorial/start.md +++ b/guides/tutorial/start.md @@ -7,7 +7,7 @@ Absinthe. Before you start, it's a good idea to have some background into GraphQL in general. Here are a few resources that might be helpful: -- The official [GraphQL](http://graphql.org/) website +- The official [GraphQL](https://graphql.org/) website - [How to GraphQL](https://www.howtographql.com/) (this includes another [brief tutorial](https://www.howtographql.com/graphql-elixir/0-introduction/) using Absinthe) ## The Example diff --git a/lib/absinthe.ex b/lib/absinthe.ex index 45177bae4e..122afb8bb0 100644 --- a/lib/absinthe.ex +++ b/lib/absinthe.ex @@ -3,7 +3,7 @@ defmodule Absinthe do Documentation for the Absinthe package, a toolkit for building GraphQL APIs with Elixir. - For usage information, see [the documentation](http://hexdocs.pm/absinthe), which + For usage information, see [the documentation](https://hexdocs.pm/absinthe), which includes guides, API information for important modules, and links to useful resources. """ diff --git a/lib/absinthe/type/scalar.ex b/lib/absinthe/type/scalar.ex index 61ee4be34c..98e1ca3ff2 100644 --- a/lib/absinthe/type/scalar.ex +++ b/lib/absinthe/type/scalar.ex @@ -19,7 +19,7 @@ defmodule Absinthe.Type.Scalar do ## Examples - Supporting a time format in ISOz format, using [Timex](http://hexdocs.pm/timex): + Supporting a time format in ISOz format, using [Timex](https://hexdocs.pm/timex): ``` scalar :time do diff --git a/test/support/fixtures/fake_definition.graphql b/test/support/fixtures/fake_definition.graphql index fc50419c76..feff79b279 100644 --- a/test/support/fixtures/fake_definition.graphql +++ b/test/support/fixtures/fake_definition.graphql @@ -184,7 +184,7 @@ input fake__options { # Only for type `lorem` loremSize: fake__loremSize # Only for types `*Date`. Example value: `YYYY MM DD`. - # [Full Specification](http://momentjs.com/docs/#/displaying/format/) + # [Full Specification](https://momentjs.com/docs/#/displaying/format/) dateFormat: String # Only for type `colorHex`. [Details here](https://stackoverflow.com/a/43235/4989887) baseColor: fake__color = { red255: 0, green255: 0, blue255: 0 } From c652ae8c5954c6e1afb3761fb3a51354f9574704 Mon Sep 17 00:00:00 2001 From: Francisco Marques Date: Mon, 10 Oct 2022 12:30:36 +0100 Subject: [PATCH 016/120] fix doc: removed double spaces --- guides/testing.md | 2 +- lib/absinthe/type/custom.ex | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/guides/testing.md b/guides/testing.md index cebbc90365..fb7b5d6852 100644 --- a/guides/testing.md +++ b/guides/testing.md @@ -89,7 +89,7 @@ end ``` Phoenix generates the `MyAppWeb.ConnCase` test helper module. This supplies the -`conn` variable containing the request and response. It also has helper functions +`conn` variable containing the request and response. It also has helper functions such as [`post/3`](https://hexdocs.pm/phoenix/Phoenix.ConnTest.html#post/3) and [`json_response/2`](https://hexdocs.pm/phoenix/Phoenix.ConnTest.html#json_response/2). diff --git a/lib/absinthe/type/custom.ex b/lib/absinthe/type/custom.ex index 01ed02a029..86c7fd8a94 100644 --- a/lib/absinthe/type/custom.ex +++ b/lib/absinthe/type/custom.ex @@ -61,7 +61,7 @@ defmodule Absinthe.Type.Custom do scalar :decimal do description """ The `Decimal` scalar type represents signed double-precision fractional - values parsed by the `Decimal` library. The Decimal appears in a JSON + values parsed by the `Decimal` library. The Decimal appears in a JSON response as a string to preserve precision. """ From d5af25848cedfc0875e12ef4466b089c04a55a60 Mon Sep 17 00:00:00 2001 From: Francisco Marques Date: Mon, 10 Oct 2022 12:32:16 +0100 Subject: [PATCH 017/120] upgrade timex documentation to use the new syntax --- lib/absinthe/schema/notation.ex | 6 +++--- lib/absinthe/type/scalar.ex | 10 +++++----- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/lib/absinthe/schema/notation.ex b/lib/absinthe/schema/notation.ex index 56bc5b9d54..5503bd3123 100644 --- a/lib/absinthe/schema/notation.ex +++ b/lib/absinthe/schema/notation.ex @@ -790,9 +790,9 @@ defmodule Absinthe.Schema.Notation do ## Examples ``` - scalar :time, description: "ISOz time" do - parse &Timex.parse(&1.value, "{ISOz}") - serialize &Timex.format!(&1, "{ISOz}") + scalar :isoz_datetime, description: "UTC only ISO8601 date time" do + parse &Timex.parse(&1, "{ISO:Extended:Z}") + serialize &Timex.format!(&1, "{ISO:Extended:Z}") end ``` """ diff --git a/lib/absinthe/type/scalar.ex b/lib/absinthe/type/scalar.ex index 98e1ca3ff2..69ef7c5a38 100644 --- a/lib/absinthe/type/scalar.ex +++ b/lib/absinthe/type/scalar.ex @@ -19,13 +19,13 @@ defmodule Absinthe.Type.Scalar do ## Examples - Supporting a time format in ISOz format, using [Timex](https://hexdocs.pm/timex): + Supporting datetime in the ISO8601 format for UTC only, using [Timex](https://hexdocs.pm/timex): ``` - scalar :time do - description "Time (in ISOz format)" - parse &Timex.DateFormat.parse(&1, "{ISOz}") - serialize &Timex.DateFormat.format!(&1, "{ISOz}") + scalar :isoz_datetime do + description "UTC only ISO8601 date time" + parse &Timex.parse(&1, "{ISO:Extended:Z}") + serialize &Timex.format!(&1, "{ISO:Extended:Z}") end ``` """ From 6975b45621f19d5723bc4864ac112f988e7873c8 Mon Sep 17 00:00:00 2001 From: Francisco Marques Date: Mon, 10 Oct 2022 12:37:11 +0100 Subject: [PATCH 018/120] removed duplicate module from documentation index Actually when mix_doc renders it removed duplicates, keeping only the latest entry. Regardless it does not make much sense to have it two times here. I believe this was a mistake, and thus removed it. I have also checked and all others are unique and are ordered (a-z) --- mix.exs | 1 - 1 file changed, 1 deletion(-) diff --git a/mix.exs b/mix.exs index b7f14bf10d..8641e7d8e9 100644 --- a/mix.exs +++ b/mix.exs @@ -152,7 +152,6 @@ defmodule Absinthe.Mixfile do Absinthe.Type, Absinthe.Type.Custom, Absinthe.Type.Argument, - Absinthe.Type.Custom, Absinthe.Type.Directive, Absinthe.Type.Enum, Absinthe.Type.Enum.Value, From 70bd17dcd443cc6b1edee1ce1ca0e74d0ffd5644 Mon Sep 17 00:00:00 2001 From: c-brenn Date: Thu, 13 Oct 2022 11:42:37 +0100 Subject: [PATCH 019/120] Allow `Registry` compression to be configured by users At the moment `Registry` is started with `compressed: true` hard coded. This presented problems when we updated absinthe some time ago, we are running an application with a large number of subscriptions and so the added CPU utilization was quite large. To avoid having to scale up we pinned absinthe to a pre-compression version. We'd like to be able to keep up to date, and so propose this change to allow users to opt-out of compression and would welcome your feedback. This PR alters the type signature of `Absinthe.Subscription.child_spec/1` so users can pass more configuration options. The original argument (a single pub-sub) is still supported for backwards compatibility. --- guides/subscriptions.md | 5 ++- guides/tutorial/subscriptions.md | 2 +- lib/absinthe/subscription.ex | 36 +++++++++++++++++-- lib/absinthe/subscription/supervisor.ex | 13 ++++--- test/absinthe/execution/subscription_test.exs | 2 +- 5 files changed, 47 insertions(+), 11 deletions(-) diff --git a/guides/subscriptions.md b/guides/subscriptions.md index 9deeb22547..e6b344b879 100644 --- a/guides/subscriptions.md +++ b/guides/subscriptions.md @@ -36,7 +36,7 @@ line: {Phoenix.PubSub, name: :my_pubsub}, # Start the endpoint when the application starts MyAppWeb.Endpoint, - {Absinthe.Subscription, MyAppWeb.Endpoint} + {Absinthe.Subscription, pubsub: MyAppWeb.Endpoint} ] # See https://hexdocs.pm/elixir/Supervisor.html @@ -45,6 +45,9 @@ line: Supervisor.start_link(children, opts) ``` +See `Absinthe.Subscription.child_spec/1` for more information on the supported +options. + In your `MyAppWeb.Endpoint` module add: ```elixir diff --git a/guides/tutorial/subscriptions.md b/guides/tutorial/subscriptions.md index b591397992..1000a2007b 100644 --- a/guides/tutorial/subscriptions.md +++ b/guides/tutorial/subscriptions.md @@ -33,7 +33,7 @@ In `lib/blog/application.ex`: children = [ # other children ... {BlogWeb.Endpoint, []}, # this line should already exist - {Absinthe.Subscription, [BlogWeb.Endpoint]}, # add this line + {Absinthe.Subscription, pubsub: BlogWeb.Endpoint}, # add this line # other children ... ] ``` diff --git a/lib/absinthe/subscription.ex b/lib/absinthe/subscription.ex index 1a83cf300f..09864ce08a 100644 --- a/lib/absinthe/subscription.ex +++ b/lib/absinthe/subscription.ex @@ -35,12 +35,42 @@ defmodule Absinthe.Subscription do @doc """ Add Absinthe.Subscription to your process tree. """ - defdelegate start_link(pubsub), to: Subscription.Supervisor + defdelegate start_link(opts), to: Subscription.Supervisor - def child_spec(pubsub) do + @type opt() :: + {:pubsub, atom()} | {:compress_registry?, boolean()} | {:pool_size, pos_integer()} + + @doc """ + Build a child specification for subscriptions. + + In order to use supscriptions in your application, you must add + `Absinthe.Subscription` to your supervision tree after your endpoint. + + See `guides/subscriptions.md` for more information on how to get up and + running with subscriptions. + + ## Options + + * `:pubsub` - (Required) The `Phoenix.Pubsub` that should be used to publish + subscriptions. Typically this will be your `Phoenix.Endpoint`. + * `:compress_registry?` - (Optional - default `true`) A boolean controlling + whether the Registry used to keep track of subscriptions will should be + compressed or not. + * `:pool_size` - (Optional - default `System.schedulers() * 2`) An integer + specifying the number of `Absinthe.Subscription.Proxy` processes to start. + """ + @spec child_spec(atom() | [opt()]) :: Supervisor.child_spec() + def child_spec(pubsub) when is_atom(pubsub) do + # child_spec/1 used to take a single argument - the pub-sub - so in order + # to maintain compatibility for existing users of the library we still + # accept this argument and transform it into a keyword list. + child_spec(pubsub: pubsub) + end + + def child_spec(opts) when is_list(opts) do %{ id: __MODULE__, - start: {Subscription.Supervisor, :start_link, [pubsub]}, + start: {Subscription.Supervisor, :start_link, [opts]}, type: :supervisor } end diff --git a/lib/absinthe/subscription/supervisor.ex b/lib/absinthe/subscription/supervisor.ex index c339ea8636..fe0060c50e 100644 --- a/lib/absinthe/subscription/supervisor.ex +++ b/lib/absinthe/subscription/supervisor.ex @@ -3,9 +3,9 @@ defmodule Absinthe.Subscription.Supervisor do use Supervisor - def start_link(pubsub, pool_size \\ System.schedulers_online() * 2) do + def start_link(opts) do pubsub = - case pubsub do + case Keyword.fetch!(opts, :pubsub) do [module] when is_atom(module) -> module @@ -13,10 +13,13 @@ defmodule Absinthe.Subscription.Supervisor do module end - Supervisor.start_link(__MODULE__, {pubsub, pool_size}) + pool_size = Keyword.get(opts, :pool_size, System.schedulers_online() * 2) + compress_registry? = Keyword.get(opts, :compress_registry?, true) + + Supervisor.start_link(__MODULE__, {pubsub, pool_size, compress_registry?}) end - def init({pubsub, pool_size}) do + def init({pubsub, pool_size, compress_registry?}) do registry_name = Absinthe.Subscription.registry_name(pubsub) meta = [pool_size: pool_size] @@ -27,7 +30,7 @@ defmodule Absinthe.Subscription.Supervisor do name: registry_name, partitions: System.schedulers_online(), meta: meta, - compressed: true + compressed: compress_registry? ]}, {Absinthe.Subscription.ProxySupervisor, [pubsub, registry_name, pool_size]} ] diff --git a/test/absinthe/execution/subscription_test.exs b/test/absinthe/execution/subscription_test.exs index 28233c0d4b..a2f92efc6c 100644 --- a/test/absinthe/execution/subscription_test.exs +++ b/test/absinthe/execution/subscription_test.exs @@ -146,7 +146,7 @@ defmodule Absinthe.Execution.SubscriptionTest do setup_all do {:ok, _} = PubSub.start_link() - {:ok, _} = Absinthe.Subscription.start_link(PubSub) + {:ok, _} = Absinthe.Subscription.start_link(pubsub: PubSub) :ok end From f617cf40367bed55b4284ef97b2bb465d1c3841d Mon Sep 17 00:00:00 2001 From: Francisco Marques Date: Tue, 18 Oct 2022 10:28:09 +0100 Subject: [PATCH 020/120] Improve Absinthe.Type.Scalar documentation --- lib/absinthe/type/scalar.ex | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/lib/absinthe/type/scalar.ex b/lib/absinthe/type/scalar.ex index 69ef7c5a38..aabf0b26ed 100644 --- a/lib/absinthe/type/scalar.ex +++ b/lib/absinthe/type/scalar.ex @@ -17,15 +17,23 @@ defmodule Absinthe.Type.Scalar do * `:integer` - Represents non-fractional signed whole numeric value. **By default it is not compliant with the GraphQl Specification**, it can represent values between `-(2^53 - 1)` and `2^53 - 1` as specified by [IEEE 754](https://en.wikipedia.org/wiki/IEEE_754). To use the [GraphQl compliant Int](https://spec.graphql.org/October2021/#sec-Int), representing values between `-2^31` and `2^31 - 1`, in your schema `use Absinthe.Schema, use_spec_compliant_int_scalar: true`. * `:string` - Represents textual data, represented as UTF-8 character sequences. The String type is most often used by GraphQL to represent free-form human-readable text. See the [GraphQL Specification](https://www.graphql.org/learn/schema/#scalar-types). - ## Examples - Supporting datetime in the ISO8601 format for UTC only, using [Timex](https://hexdocs.pm/timex): + ## Built-in custom types + + Note that absinthe includes a few generic extra scalar types in `Absinthe.Type.Custom`, + these are not part of the GraphQL spec thus they must be explicitly imported. + Consider using them before implementing a custom type. + + + ## Example + + Supporting ISO8601 week date format, using [Timex](https://hexdocs.pm/timex): ``` - scalar :isoz_datetime do - description "UTC only ISO8601 date time" - parse &Timex.parse(&1, "{ISO:Extended:Z}") - serialize &Timex.format!(&1, "{ISO:Extended:Z}") + scalar :iso_week_date do + description "ISO8601 week date (ex: 2022-W42-2)" + parse &Timex.parse(&1, "{YYYY}-W{Wiso}-{WDmon}") + serialize &Timex.format!(&1, "{YYYY}-W{Wiso}-{WDmon}") end ``` """ From c83152cba8d75fed50882a6f4103d3b12c9b2d45 Mon Sep 17 00:00:00 2001 From: Maarten van Vliet Date: Tue, 8 Nov 2022 14:29:21 +0100 Subject: [PATCH 021/120] Fix const parsing of empty lists An empty list is a valid const value. This fixes the parsing of empty lists. --- src/absinthe_parser.yrl | 1 + test/absinthe/phase/parse/const_usage_test.exs | 18 ++++++++++++++++-- 2 files changed, 17 insertions(+), 2 deletions(-) diff --git a/src/absinthe_parser.yrl b/src/absinthe_parser.yrl index 4036603a3d..a07f9f0910 100644 --- a/src/absinthe_parser.yrl +++ b/src/absinthe_parser.yrl @@ -178,6 +178,7 @@ ValueConst -> ObjectValueConst : build_ast_node('ObjectValue', #{'fields' => EnumValue -> Name : extract_binary('$1'). +ListValueConst -> '[' ']' : []. ListValueConst -> '[' ValuesConst ']' : '$2'. ValuesConst -> ValueConst : ['$1']. ValuesConst -> ValueConst ValuesConst : ['$1'|'$2']. diff --git a/test/absinthe/phase/parse/const_usage_test.exs b/test/absinthe/phase/parse/const_usage_test.exs index faaca97086..4bae0a3145 100644 --- a/test/absinthe/phase/parse/const_usage_test.exs +++ b/test/absinthe/phase/parse/const_usage_test.exs @@ -4,6 +4,18 @@ defmodule Absinthe.Phase.Parse.ConstUsageTest do @moduletag :parser describe "composed constants" do + test "list in a constant location can be empty " do + result = + """ + schema @feature(name: []){ + query: Query + } + """ + |> run + + assert {:ok, _} = result + end + test "list in a constant location cannot contain variables " do result = """ @@ -181,7 +193,9 @@ defmodule Absinthe.Phase.Parse.ConstUsageTest do end def run(input) do - {:error, blueprint} = Absinthe.Phase.Parse.run(input) - {:error, blueprint.execution.validation_errors} + case Absinthe.Phase.Parse.run(input) do + {:error, blueprint} -> {:error, blueprint.execution.validation_errors} + {:ok, blueprint} -> {:ok, blueprint} + end end end From d456298f5e12f3d05c8801437ae0aa809f344a63 Mon Sep 17 00:00:00 2001 From: Marcin Koziej Date: Wed, 16 Nov 2022 19:00:41 +0100 Subject: [PATCH 022/120] Fix bookkeeping of Resolution path when resolving Addresses issue #1149. module `Absinthe.Phase.Document.Execution.Resolution` walks the result tree and tracks `path` using a function parameter. At the same time, a `Resolution` struct is passed, which `path` field is not consistently tracked. The problem manifests itself in bogus Resolution path passed onto `resolve_type` function as described in function #1149. The reason for not updating the resolution.path in the walk process can be explained by comment of @benwilson512: `Traditionally the reason we pass in the env has more to do with making the schema available and the context available, I don't know that the other values in that struct have been validated in this situation.` Perhaps path parameter was used in the beginning and later resolution paramter was added, but not used to track current tree walk in it's path field. Nevertheless, the resolution is passed on to user code - into `resolve_type(value, resolution)` callback, which might use path to understand the context of value (eg. what is the type of parent?). This PR fixes the bookkeeping of `resolution.path` field. I have not inspected the use of other field, I am not sure which ones would change during the value walk (`parent_type` maybe?). One could consider to just use `resolution.path` instead of `path` parameter - so there is not double bookkeeping of this value. This is up to maintainers to decide, I did not make such a change. --- lib/absinthe/phase/document/execution/resolution.ex | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/lib/absinthe/phase/document/execution/resolution.ex b/lib/absinthe/phase/document/execution/resolution.ex index bde57714c7..82ea404ae2 100644 --- a/lib/absinthe/phase/document/execution/resolution.ex +++ b/lib/absinthe/phase/document/execution/resolution.ex @@ -111,11 +111,11 @@ defmodule Absinthe.Phase.Document.Execution.Resolution do # walk list results defp walk_results([value | values], bp_node, inner_type, res, [i | sub_path] = path, acc) do - {result, res} = walk_result(value, bp_node, inner_type, res, path) + {result, res} = walk_result(value, bp_node, inner_type, %{res | path: path}, path) walk_results(values, bp_node, inner_type, res, [i + 1 | sub_path], [result | acc]) end - defp walk_results([], _, _, res, _, acc), do: {:lists.reverse(acc), res} + defp walk_results([], _, _, res = %{path: [_ | sub_path]}, _, acc), do: {:lists.reverse(acc), %{res | path: sub_path}} defp resolve_fields(parent, res, source, path) do # parent is the parent field, we need to get the return type of that field @@ -139,7 +139,8 @@ defmodule Absinthe.Phase.Document.Execution.Resolution do res = %{res | fields_cache: fields_cache} - do_resolve_fields(fields, res, source, parent_type, path, []) + {values, res} = do_resolve_fields(fields, res, source, parent_type, path, []) + {values, %{res | path: path}} end end From 161ed3cbd106b67beed4a6cff8ee32324e5669ea Mon Sep 17 00:00:00 2001 From: JasonMiller Date: Thu, 17 Nov 2022 00:00:00 -0500 Subject: [PATCH 023/120] fix Our First Query screenshot --- guides/tutorial/our-first-query.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/guides/tutorial/our-first-query.md b/guides/tutorial/our-first-query.md index 8cf676d5b1..b51bb0f28b 100644 --- a/guides/tutorial/our-first-query.md +++ b/guides/tutorial/our-first-query.md @@ -156,7 +156,7 @@ Once it's up-and-running, take a look at [http://localhost:4000/api/graphiql](ht Make sure that the `URL` is pointing to the correct place and press the play button. If everything goes according to plan, you should see something like this: - + ## Next Step From ac9548a3fbbc564f89c98e531d4b5294e6ab405a Mon Sep 17 00:00:00 2001 From: Marcin Koziej Date: Thu, 17 Nov 2022 20:34:41 +0100 Subject: [PATCH 024/120] Fix formatting with mix format --- lib/absinthe/phase/document/execution/resolution.ex | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/absinthe/phase/document/execution/resolution.ex b/lib/absinthe/phase/document/execution/resolution.ex index 82ea404ae2..d15ddd9cc1 100644 --- a/lib/absinthe/phase/document/execution/resolution.ex +++ b/lib/absinthe/phase/document/execution/resolution.ex @@ -115,7 +115,8 @@ defmodule Absinthe.Phase.Document.Execution.Resolution do walk_results(values, bp_node, inner_type, res, [i + 1 | sub_path], [result | acc]) end - defp walk_results([], _, _, res = %{path: [_ | sub_path]}, _, acc), do: {:lists.reverse(acc), %{res | path: sub_path}} + defp walk_results([], _, _, res = %{path: [_ | sub_path]}, _, acc), + do: {:lists.reverse(acc), %{res | path: sub_path}} defp resolve_fields(parent, res, source, path) do # parent is the parent field, we need to get the return type of that field From 703c23f80bd6b5123b90f59f84738d4de4b8467c Mon Sep 17 00:00:00 2001 From: Marcin Koziej Date: Thu, 17 Nov 2022 20:34:53 +0100 Subject: [PATCH 025/120] Add test for issue #1149 --- test/absinthe/type/interface_test.exs | 125 ++++++++++++++++++++++++++ 1 file changed, 125 insertions(+) diff --git a/test/absinthe/type/interface_test.exs b/test/absinthe/type/interface_test.exs index 59cd4f9ffa..43393fb1ce 100644 --- a/test/absinthe/type/interface_test.exs +++ b/test/absinthe/type/interface_test.exs @@ -316,4 +316,129 @@ defmodule Absinthe.Type.InterfaceTest do test "resolved type of nested interfaces" do assert_data(%{"root" => %{"__typename" => "ZChild"}}, run(@graphql, NestedInterfacesSchema)) end + + defmodule PrivacyUsingNestedSchema do + @moduledoc """ + Schema in which we return + public or private view of collections depending on auth status, and for + items depending on collection. PrivateItem will be returned for items in + PrivateCollection + + """ + use Absinthe.Schema + + @data [ + %{ + name: "Travel Books", + items: [ + %{title: "To the moon and back", content: "How to build a rocket"}, + %{title: "Ends of the world", content: "How it looks in the end?"} + ] + }, + %{ + name: "Cuisine", + items: [ + %{title: "Polish soups", content: "All on pomidorowa soup"}, + %{title: "Only sweets", content: "Carb diet for the win!"} + ] + } + ] + + def data(), do: @data + + query do + field :collections, list_of(:collection) do + resolve fn _, _, _ -> + {:ok, @data} + end + end + end + + interface :collection do + description "A collection" + field :name, non_null(:string) + + resolve_type fn value, %{context: %{auth: is_auth}} -> + if is_auth, do: :private_collection, else: :public_collection + end + end + + object :public_collection do + interface :collection + import_fields :collection + end + + object :private_collection do + interface :collection + import_fields :collection + field :items, list_of(:item) + end + + interface :item do + description "An item" + field :title, non_null(:string) + + resolve_type fn value, %{path: path} -> + assert [ + idx, + %{name: "items", parent_type: %{identifier: parent_id}}, + outer_idx, + %{name: "collections"} | _ + ] = path + + assert idx in 0..1 + assert outer_idx in 0..1 + assert parent_id == :private_collection + + if parent_id == :private_collection, do: :private_item, else: :public_item + end + end + + object :public_item do + interface :item + import_fields :item + end + + object :private_item do + interface :item + import_fields :item + field :content, :string + end + end + + # deep convert keys from atoms to strings - is it available somewhere in library? + defp stringify_keys(v) when is_list(v) do + Enum.map(v, &stringify_keys/1) + end + + defp stringify_keys(v) when is_map(v) do + Enum.into(Enum.map(v, fn {k, v} -> {Atom.to_string(k), stringify_keys(v)} end), %{}) + end + + defp stringify_keys(v) do + v + end + + @graphql """ + query books { + collections { + name + ... on PrivateCollection { + items { + ... on PrivateItem { + title content + } + } + } + } + } + """ + test "Nested interface resolution passes correct data to resolve_type" do + stringified_data = stringify_keys(PrivacyUsingNestedSchema.data()) + + assert_data( + %{"collections" => stringified_data}, + run(@graphql, PrivacyUsingNestedSchema, context: %{auth: true}) + ) + end end From 0f20ed9af450cb101c4d1fe119348424c9ee63b8 Mon Sep 17 00:00:00 2001 From: Ellie Poley Date: Tue, 22 Nov 2022 18:32:45 -0700 Subject: [PATCH 026/120] Enforce a configurable limit on tokens in a GraphQL document to protect from DoS --- lib/absinthe/lexer.ex | 18 +++++++++++---- lib/absinthe/phase/parse.ex | 23 +++++++++++-------- .../execution/token_limit_enforcement.exs | 13 +++++++++++ test/absinthe/lexer_test.exs | 15 ++++++++++++ 4 files changed, 54 insertions(+), 15 deletions(-) create mode 100644 test/absinthe/integration/execution/token_limit_enforcement.exs diff --git a/lib/absinthe/lexer.ex b/lib/absinthe/lexer.ex index a481e53c12..6f6a621ebc 100644 --- a/lib/absinthe/lexer.ex +++ b/lib/absinthe/lexer.ex @@ -227,21 +227,29 @@ defmodule Absinthe.Lexer do {:cont, context} end - @spec tokenize(binary()) :: {:ok, [any()]} | {:error, binary(), {integer(), non_neg_integer()}} - def tokenize(input) do + @spec tokenize(binary(), Map.t()) :: {:ok, [any()]} | {:error, binary(), {integer(), non_neg_integer()}} + def tokenize(input, options \\ %{}) do lines = String.split(input, ~r/\r?\n/) case do_tokenize(input) do {:ok, tokens, "", _, _, _} -> - tokens = Enum.map(tokens, &convert_token_column(&1, lines)) - {:ok, tokens} - + check_limit_and_process_tokens(tokens, options, lines) {:ok, _, rest, _, {line, line_offset}, byte_offset} -> byte_column = byte_offset - line_offset + 1 {:error, rest, byte_loc_to_char_loc({line, byte_column}, lines)} end end + defp check_limit_and_process_tokens(tokens, options, lines) do + limit = Map.get(options, :token_limit, 2000) + if Enum.count(tokens) > limit do + {:error, :exceeded_token_limit} + else + tokens = Enum.map(tokens, &convert_token_column(&1, lines)) + {:ok, tokens} + end + end + defp convert_token_column({ident, loc, data}, lines) do {ident, byte_loc_to_char_loc(loc, lines), data} end diff --git a/lib/absinthe/phase/parse.ex b/lib/absinthe/phase/parse.ex index 7b04faf130..5cace7eb14 100644 --- a/lib/absinthe/phase/parse.ex +++ b/lib/absinthe/phase/parse.ex @@ -14,7 +14,7 @@ defmodule Absinthe.Phase.Parse do def run(%Absinthe.Blueprint{} = blueprint, options) do options = Map.new(options) - case parse(blueprint.input) do + case parse(blueprint.input, options) do {:ok, value} -> {:ok, %{blueprint | input: value}} @@ -44,12 +44,15 @@ defmodule Absinthe.Phase.Parse do {:error, blueprint} end - @spec tokenize(binary) :: {:ok, [tuple]} | {:error, String.t()} - def tokenize(input) do - case Absinthe.Lexer.tokenize(input) do + @spec tokenize(binary, Map.t()) :: {:ok, [tuple]} | {:error, String.t()} + def tokenize(input, options \\ %{}) do + case Absinthe.Lexer.tokenize(input, options) do {:error, rest, loc} -> {:error, format_raw_parse_error({:lexer, rest, loc})} + {:error, :exceeded_token_limit} -> + {:error, %Phase.Error{message: "Token limit exceeded", phase: __MODULE__}} + other -> other end @@ -57,15 +60,15 @@ defmodule Absinthe.Phase.Parse do # This is because Dialyzer is telling us tokenizing can never fail, # but we know it's possible. - @dialyzer {:no_match, parse: 1} - @spec parse(binary | Language.Source.t()) :: {:ok, Language.Document.t()} | {:error, tuple} - defp parse(input) when is_binary(input) do - parse(%Language.Source{body: input}) + @dialyzer {:no_match, parse: 2} + @spec parse(binary | Language.Source.t(), Map.t()) :: {:ok, Language.Document.t()} | {:error, tuple} + defp parse(input, options) when is_binary(input) do + parse(%Language.Source{body: input}, options) end - defp parse(input) do + defp parse(input, options) do try do - case tokenize(input.body) do + case tokenize(input.body, options) do {:ok, []} -> {:ok, %Language.Document{}} diff --git a/test/absinthe/integration/execution/token_limit_enforcement.exs b/test/absinthe/integration/execution/token_limit_enforcement.exs new file mode 100644 index 0000000000..8b110d5d0d --- /dev/null +++ b/test/absinthe/integration/execution/token_limit_enforcement.exs @@ -0,0 +1,13 @@ +defmodule Elixir.Absinthe.Integration.Execution.TokenLimitEnforcement do + use Absinthe.Case, async: true + + @query """ + { + __typename @a @b @c @d @e + } + """ + test "Token limit lexer enforcement" do + assert {:ok, %{errors: [%{message: "Token limit exceeded"}]}} == + Absinthe.run(@query, Absinthe.Fixtures.Things.MacroSchema, [token_limit: 4]) + end + end diff --git a/test/absinthe/lexer_test.exs b/test/absinthe/lexer_test.exs index a56f4c0015..d27c363282 100644 --- a/test/absinthe/lexer_test.exs +++ b/test/absinthe/lexer_test.exs @@ -94,4 +94,19 @@ defmodule Absinthe.LexerTest do {:"}", {7, 1}} ]} == Absinthe.Lexer.tokenize(@query) end + + test "document with tokens exceeding limit" do + assert {:error, :exceeded_token_limit} == Absinthe.Lexer.tokenize(too_long_query()) + end + + defp too_long_query do + Enum.to_list(for n <- 1..10000, do: "test#{n}") + |> deep_query() + end + + defp deep_query([]), do: "" + defp deep_query([field | rest]) do + "{ #{field} #{deep_query(rest)} }" + end + end From 918a4ea9807ad78b1b4cb98c605ea9e15ae1f428 Mon Sep 17 00:00:00 2001 From: Ellie Poley Date: Tue, 22 Nov 2022 20:46:10 -0700 Subject: [PATCH 027/120] Set the default token limit to 15_000, matching graphql-java's implementation of the same --- lib/absinthe/lexer.ex | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/absinthe/lexer.ex b/lib/absinthe/lexer.ex index 6f6a621ebc..55b8f32801 100644 --- a/lib/absinthe/lexer.ex +++ b/lib/absinthe/lexer.ex @@ -241,7 +241,7 @@ defmodule Absinthe.Lexer do end defp check_limit_and_process_tokens(tokens, options, lines) do - limit = Map.get(options, :token_limit, 2000) + limit = Map.get(options, :token_limit, 15_000) if Enum.count(tokens) > limit do {:error, :exceeded_token_limit} else From 9e5c5ae1ed4ae4736bd69f5e4711c804ccd0c6f7 Mon Sep 17 00:00:00 2001 From: Ellie Poley Date: Wed, 23 Nov 2022 13:48:45 -0700 Subject: [PATCH 028/120] Include token limits in the documentation --- guides/complexity-analysis.md | 24 ++++++++++++++++++++++-- 1 file changed, 22 insertions(+), 2 deletions(-) diff --git a/guides/complexity-analysis.md b/guides/complexity-analysis.md index 8fddec67c0..e438e1b090 100644 --- a/guides/complexity-analysis.md +++ b/guides/complexity-analysis.md @@ -1,7 +1,18 @@ -# Complexity Analysis +# Complexity Analysis and Token Limits A misbehaving client might send a very complex GraphQL query that would require -considerable resources to handle. In order to protect against this scenario, the +considerable resources to handle. There are two variations of this problem: + +- Complex queries that overwhelm resolution resources +- Long, invalid queries that could take considerable resources to parse, even if +the response is only validation errors. + +Either of these could be a vector for a denial-of-service attack in any GraphQL +service. Absinthe includes mechanisms to protect services each of these. + +## Complexity Analysis + +In order to protect against queries that could overwhel scenario, the complexity of a query can be estimated before it is resolved and limited to a specified maximum. @@ -105,3 +116,12 @@ But this, at a complexity of `60`, wouldn't: If a document's calculated complexity exceeds the configured limit, resolution will be skipped and an error will be returned in the result detailing the calculated and maximum complexities. + +## Token Limits + +To protect a service from invalid queries that could take considerable resources to parse, +Absinthe includes a maximum limit on tokens in the GraphQL request document. If the lexer +encounters more tokens than this, it will stop the parse phase and return a phase error +with the message `"Token limit exceeded"`. This limit is 10,000 by default and can be +overridden by providing the option `token_limit` to `Absinthe.run`. `token_limit` could +be an integer or `:infinity` for no limit. From 75612b53bf6f58c148e831deff97e21462892546 Mon Sep 17 00:00:00 2001 From: Ellie Poley Date: Wed, 23 Nov 2022 13:50:48 -0700 Subject: [PATCH 029/120] Fix limit typo in docs --- guides/complexity-analysis.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/guides/complexity-analysis.md b/guides/complexity-analysis.md index e438e1b090..ab95a0af39 100644 --- a/guides/complexity-analysis.md +++ b/guides/complexity-analysis.md @@ -122,6 +122,6 @@ calculated and maximum complexities. To protect a service from invalid queries that could take considerable resources to parse, Absinthe includes a maximum limit on tokens in the GraphQL request document. If the lexer encounters more tokens than this, it will stop the parse phase and return a phase error -with the message `"Token limit exceeded"`. This limit is 10,000 by default and can be +with the message `"Token limit exceeded"`. This limit is 15,000 by default and can be overridden by providing the option `token_limit` to `Absinthe.run`. `token_limit` could be an integer or `:infinity` for no limit. From 0a32426d8241fa729fd6f0f4a02714908dba3077 Mon Sep 17 00:00:00 2001 From: Ellie Poley Date: Wed, 23 Nov 2022 17:16:58 -0700 Subject: [PATCH 030/120] Avoid these options being passed through as a map --- lib/absinthe/lexer.ex | 8 ++++---- lib/absinthe/phase/parse.ex | 8 +++----- 2 files changed, 7 insertions(+), 9 deletions(-) diff --git a/lib/absinthe/lexer.ex b/lib/absinthe/lexer.ex index 55b8f32801..bcfedc455f 100644 --- a/lib/absinthe/lexer.ex +++ b/lib/absinthe/lexer.ex @@ -227,8 +227,8 @@ defmodule Absinthe.Lexer do {:cont, context} end - @spec tokenize(binary(), Map.t()) :: {:ok, [any()]} | {:error, binary(), {integer(), non_neg_integer()}} - def tokenize(input, options \\ %{}) do + @spec tokenize(binary(), Keyword.t()) :: {:ok, [any()]} | {:error, binary(), {integer(), non_neg_integer()}} + def tokenize(input, options \\ []) do lines = String.split(input, ~r/\r?\n/) case do_tokenize(input) do @@ -241,8 +241,8 @@ defmodule Absinthe.Lexer do end defp check_limit_and_process_tokens(tokens, options, lines) do - limit = Map.get(options, :token_limit, 15_000) - if Enum.count(tokens) > limit do + limit = Keyword.get(options, :token_limit, 15_000) + if length(tokens) > limit do {:error, :exceeded_token_limit} else tokens = Enum.map(tokens, &convert_token_column(&1, lines)) diff --git a/lib/absinthe/phase/parse.ex b/lib/absinthe/phase/parse.ex index 5cace7eb14..7214d25d92 100644 --- a/lib/absinthe/phase/parse.ex +++ b/lib/absinthe/phase/parse.ex @@ -12,8 +12,6 @@ defmodule Absinthe.Phase.Parse do def run(input, options \\ []) def run(%Absinthe.Blueprint{} = blueprint, options) do - options = Map.new(options) - case parse(blueprint.input, options) do {:ok, value} -> {:ok, %{blueprint | input: value}} @@ -21,7 +19,7 @@ defmodule Absinthe.Phase.Parse do {:error, error} -> blueprint |> add_validation_error(error) - |> handle_error(options) + |> handle_error(Map.new(options)) end end @@ -44,8 +42,8 @@ defmodule Absinthe.Phase.Parse do {:error, blueprint} end - @spec tokenize(binary, Map.t()) :: {:ok, [tuple]} | {:error, String.t()} - def tokenize(input, options \\ %{}) do + @spec tokenize(binary, Keyword.t()) :: {:ok, [tuple]} | {:error, String.t()} + def tokenize(input, options \\ []) do case Absinthe.Lexer.tokenize(input, options) do {:error, rest, loc} -> {:error, format_raw_parse_error({:lexer, rest, loc})} From 4824b6dc250a99ec252da9159fb70379405b885b Mon Sep 17 00:00:00 2001 From: Ellie Poley Date: Wed, 23 Nov 2022 17:22:58 -0700 Subject: [PATCH 031/120] Tweak docs --- guides/complexity-analysis.md | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/guides/complexity-analysis.md b/guides/complexity-analysis.md index ab95a0af39..f51613dad9 100644 --- a/guides/complexity-analysis.md +++ b/guides/complexity-analysis.md @@ -1,11 +1,12 @@ -# Complexity Analysis and Token Limits +# Safety Limits A misbehaving client might send a very complex GraphQL query that would require considerable resources to handle. There are two variations of this problem: -- Complex queries that overwhelm resolution resources -- Long, invalid queries that could take considerable resources to parse, even if -the response is only validation errors. +- Complex queries that overwhelm resolution resources. +- Extremely long queries that could take considerable resources to parse. +(For example, an attacker could craft a long query including thousands of +undefined fields or directives.) Either of these could be a vector for a denial-of-service attack in any GraphQL service. Absinthe includes mechanisms to protect services each of these. From 92f5960e5233d7ea71e59627a6701a81926184fa Mon Sep 17 00:00:00 2001 From: Maarten van Vliet Date: Wed, 7 Sep 2022 10:50:28 +0200 Subject: [PATCH 032/120] Fix typo with quotes --- lib/absinthe/phase/document/validation/known_directives.ex | 4 ++-- .../phase/document/validation/known_directives_test.exs | 6 +++--- test/absinthe/strict_schema_test.exs | 2 +- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/lib/absinthe/phase/document/validation/known_directives.ex b/lib/absinthe/phase/document/validation/known_directives.ex index f1934afc6b..2a84b7f406 100644 --- a/lib/absinthe/phase/document/validation/known_directives.ex +++ b/lib/absinthe/phase/document/validation/known_directives.ex @@ -64,7 +64,7 @@ defmodule Absinthe.Phase.Document.Validation.KnownDirectives do defp error_unknown(node) do %Phase.Error{ phase: __MODULE__, - message: "Unknown directive `#{node.name}'.", + message: "Unknown directive `#{node.name}`.", locations: [node.source_location] } end @@ -75,7 +75,7 @@ defmodule Absinthe.Phase.Document.Validation.KnownDirectives do %Phase.Error{ phase: __MODULE__, - message: "Directive `#{node.name}' may not be used on #{placement_name}.", + message: "Directive `#{node.name}` may not be used on #{placement_name}.", locations: [node.source_location] } end diff --git a/test/absinthe/phase/document/validation/known_directives_test.exs b/test/absinthe/phase/document/validation/known_directives_test.exs index f20ce5782e..5d6da639f9 100644 --- a/test/absinthe/phase/document/validation/known_directives_test.exs +++ b/test/absinthe/phase/document/validation/known_directives_test.exs @@ -5,12 +5,12 @@ defmodule Absinthe.Phase.Document.Validation.KnownDirectivesTest do phase: @phase, async: true - alias Absinthe.{Blueprint} + alias Absinthe.Blueprint def unknown_directive(name, line) do bad_value( Blueprint.Directive, - "Unknown directive `#{name}'.", + "Unknown directive `#{name}`.", line, name: name ) @@ -19,7 +19,7 @@ defmodule Absinthe.Phase.Document.Validation.KnownDirectivesTest do def misplaced_directive(name, placement, line) do bad_value( Blueprint.Directive, - "Directive `#{name}' may not be used on #{placement}.", + "Directive `#{name}` may not be used on #{placement}.", line, name: name ) diff --git a/test/absinthe/strict_schema_test.exs b/test/absinthe/strict_schema_test.exs index 03eda113c4..d62592a0bf 100644 --- a/test/absinthe/strict_schema_test.exs +++ b/test/absinthe/strict_schema_test.exs @@ -34,7 +34,7 @@ defmodule Absinthe.StrictSchemaTest do variables = %{"input" => %{"naiveDatetime" => "2017-01-27T20:31:55"}} assert_error_message( - "Unknown directive `foo_bar_directive'.", + "Unknown directive `foo_bar_directive`.", run(document, Absinthe.Fixtures.StrictSchema, adapter: Absinthe.Adapter.StrictLanguageConventions, variables: variables From 9c269dc2ca45d8bbe821caf8934e25a1444c976a Mon Sep 17 00:00:00 2001 From: Maarten van Vliet Date: Wed, 7 Sep 2022 10:51:03 +0200 Subject: [PATCH 033/120] Bring default_name in line with other usages --- lib/absinthe/schema/notation.ex | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/lib/absinthe/schema/notation.ex b/lib/absinthe/schema/notation.ex index 5503bd3123..717e34b580 100644 --- a/lib/absinthe/schema/notation.ex +++ b/lib/absinthe/schema/notation.ex @@ -1600,7 +1600,7 @@ defmodule Absinthe.Schema.Notation do attrs = attrs |> Keyword.put(:identifier, identifier) - |> Keyword.put_new(:name, to_string(identifier)) + |> Keyword.put_new(:name, default_name(Schema.DirectiveDefinition, identifier)) |> Keyword.update(:description, nil, &wrap_in_unquote/1) scoped_def(env, Schema.DirectiveDefinition, identifier, attrs, block) @@ -1979,6 +1979,11 @@ defmodule Absinthe.Schema.Notation do |> Atom.to_string() end + defp default_name(Schema.DirectiveDefinition, identifier) do + identifier + |> Atom.to_string() + end + defp default_name(_, identifier) do identifier |> Atom.to_string() From e0bd25a57d422e5aa0849b1b5eccdd0a02794e74 Mon Sep 17 00:00:00 2001 From: Maarten van Vliet Date: Wed, 7 Sep 2022 10:51:18 +0200 Subject: [PATCH 034/120] Add specifiedBy type system directive as per spec See https://spec.graphql.org/October2021/#sec--specifiedBy for the specification Fixes https://github.com/absinthe-graphql/absinthe/issues/918 --- lib/absinthe/schema/prototype/notation.ex | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/lib/absinthe/schema/prototype/notation.ex b/lib/absinthe/schema/prototype/notation.ex index 2818d28396..b55de37338 100644 --- a/lib/absinthe/schema/prototype/notation.ex +++ b/lib/absinthe/schema/prototype/notation.ex @@ -34,6 +34,17 @@ defmodule Absinthe.Schema.Prototype.Notation do expand &__MODULE__.expand_deprecate/2 end + directive :specified_by do + description "Exposes a URL that specifies the behavior of this scalar." + + repeatable false + + arg :url, non_null(:string), + description: "The URL that specifies the behavior of this scalar." + + on [:scalar] + end + def pipeline(pipeline) do pipeline |> Absinthe.Pipeline.without(Absinthe.Phase.Schema.Validation.QueryTypeMustBeObject) From a917e3d37173ccb7d01e478d850ab1e795af6dde Mon Sep 17 00:00:00 2001 From: Maarten van Vliet Date: Wed, 7 Sep 2022 10:53:29 +0200 Subject: [PATCH 035/120] Pass directive names through language adapter --- lib/absinthe/type/built_ins/introspection.ex | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/lib/absinthe/type/built_ins/introspection.ex b/lib/absinthe/type/built_ins/introspection.ex index 4923acc230..5ffc20fe53 100644 --- a/lib/absinthe/type/built_ins/introspection.ex +++ b/lib/absinthe/type/built_ins/introspection.ex @@ -54,7 +54,11 @@ defmodule Absinthe.Type.BuiltIns.Introspection do object :__directive do description "Represents a directive" - field :name, non_null(:string) + field :name, + type: non_null(:string), + resolve: fn _, %{adapter: adapter, source: source} -> + {:ok, adapter.to_external_name(source.name, :field)} + end field :description, :string From f9501ebc59ad2298aa09a079414f4fb72ad8e607 Mon Sep 17 00:00:00 2001 From: Maarten van Vliet Date: Wed, 7 Sep 2022 10:53:55 +0200 Subject: [PATCH 036/120] Test for specifiedBy presence in introspection --- .../execution/introspection/directives_test.exs | 17 +++++++++++++++++ test/absinthe/introspection_test.exs | 10 ++++++++++ 2 files changed, 27 insertions(+) diff --git a/test/absinthe/integration/execution/introspection/directives_test.exs b/test/absinthe/integration/execution/introspection/directives_test.exs index bd779fad54..764afc0c1c 100644 --- a/test/absinthe/integration/execution/introspection/directives_test.exs +++ b/test/absinthe/integration/execution/introspection/directives_test.exs @@ -72,6 +72,23 @@ defmodule Elixir.Absinthe.Integration.Execution.Introspection.DirectivesTest do "onFragment" => true, "onOperation" => false, "isRepeatable" => false + }, + %{ + "isRepeatable" => false, + "locations" => ["SCALAR"], + "name" => "specifiedBy", + "onField" => false, + "onFragment" => false, + "onOperation" => false, + "args" => [ + %{ + "name" => "url", + "type" => %{ + "kind" => "NON_NULL", + "ofType" => %{"kind" => "SCALAR", "name" => "String"} + } + } + ] } ] } diff --git a/test/absinthe/introspection_test.exs b/test/absinthe/introspection_test.exs index 391eddfcf9..3575efb9d1 100644 --- a/test/absinthe/introspection_test.exs +++ b/test/absinthe/introspection_test.exs @@ -71,6 +71,16 @@ defmodule Absinthe.IntrospectionTest do "onField" => true, "onFragment" => true, "onOperation" => false + }, + %{ + "description" => + "Exposes a URL that specifies the behavior of this scalar.", + "isRepeatable" => false, + "locations" => ["SCALAR"], + "name" => "specifiedBy", + "onField" => false, + "onFragment" => false, + "onOperation" => false } ] } From 9b0cb1c213f07be76837120a261ab0a820c44097 Mon Sep 17 00:00:00 2001 From: Maarten van Vliet Date: Wed, 7 Sep 2022 15:27:10 +0200 Subject: [PATCH 037/120] Update changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index e85eea8208..ad5e14a799 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,7 @@ ## Unreleased - Bug Fix: [Validate type references for invalid wrapped types](https://github.com/absinthe-graphql/absinthe/pull/1195) +- Feature: [Add `specifiedBy` type system directive](https://github.com/absinthe-graphql/absinthe/pull/1193) - Breaking Bugfix: [Validate repeatable directives on schemas](https://github.com/absinthe-graphql/absinthe/pull/1179) - Bug Fix: Adds **optional fix** for non compliant built-in scalar Int type. `use Absinthe.Schema, use_spec_compliant_int_scalar: true` in your schema to use the fixed Int type. It is also advisable to upgrade for custom types if you are leveraging the use of integers outside the GraphQl standard. [#1131](https://github.com/absinthe-graphql/absinthe/pull/1131). - Feature: [Support error tuples when scalar parsing fails](https://github.com/absinthe-graphql/absinthe/pull/1187) From d4a7d9cbb6bfbad7f5ed9d4957bd0fda89d12e35 Mon Sep 17 00:00:00 2001 From: Ellie Poley Date: Wed, 30 Nov 2022 10:40:06 -0600 Subject: [PATCH 038/120] Fix typos in docs Co-authored-by: Kaden Wilkinson <7799267+kdawgwilk@users.noreply.github.com> --- guides/complexity-analysis.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/guides/complexity-analysis.md b/guides/complexity-analysis.md index f51613dad9..f370cbc9cf 100644 --- a/guides/complexity-analysis.md +++ b/guides/complexity-analysis.md @@ -13,7 +13,7 @@ service. Absinthe includes mechanisms to protect services each of these. ## Complexity Analysis -In order to protect against queries that could overwhel scenario, the +To protect against queries that could overwhelm available resources, the complexity of a query can be estimated before it is resolved and limited to a specified maximum. From 58805fa6c79c269bd0754aa2fbcfc0ec8328f68d Mon Sep 17 00:00:00 2001 From: Ellie Poley Date: Wed, 30 Nov 2022 13:20:22 -0600 Subject: [PATCH 039/120] mix format --- lib/absinthe/lexer.ex | 5 ++++- lib/absinthe/phase/parse.ex | 3 ++- .../execution/token_limit_enforcement.exs | 20 +++++++++---------- test/absinthe/lexer_test.exs | 2 +- 4 files changed, 17 insertions(+), 13 deletions(-) diff --git a/lib/absinthe/lexer.ex b/lib/absinthe/lexer.ex index bcfedc455f..3caf46258a 100644 --- a/lib/absinthe/lexer.ex +++ b/lib/absinthe/lexer.ex @@ -227,13 +227,15 @@ defmodule Absinthe.Lexer do {:cont, context} end - @spec tokenize(binary(), Keyword.t()) :: {:ok, [any()]} | {:error, binary(), {integer(), non_neg_integer()}} + @spec tokenize(binary(), Keyword.t()) :: + {:ok, [any()]} | {:error, binary(), {integer(), non_neg_integer()}} def tokenize(input, options \\ []) do lines = String.split(input, ~r/\r?\n/) case do_tokenize(input) do {:ok, tokens, "", _, _, _} -> check_limit_and_process_tokens(tokens, options, lines) + {:ok, _, rest, _, {line, line_offset}, byte_offset} -> byte_column = byte_offset - line_offset + 1 {:error, rest, byte_loc_to_char_loc({line, byte_column}, lines)} @@ -242,6 +244,7 @@ defmodule Absinthe.Lexer do defp check_limit_and_process_tokens(tokens, options, lines) do limit = Keyword.get(options, :token_limit, 15_000) + if length(tokens) > limit do {:error, :exceeded_token_limit} else diff --git a/lib/absinthe/phase/parse.ex b/lib/absinthe/phase/parse.ex index 7214d25d92..680671a13e 100644 --- a/lib/absinthe/phase/parse.ex +++ b/lib/absinthe/phase/parse.ex @@ -59,7 +59,8 @@ defmodule Absinthe.Phase.Parse do # This is because Dialyzer is telling us tokenizing can never fail, # but we know it's possible. @dialyzer {:no_match, parse: 2} - @spec parse(binary | Language.Source.t(), Map.t()) :: {:ok, Language.Document.t()} | {:error, tuple} + @spec parse(binary | Language.Source.t(), Map.t()) :: + {:ok, Language.Document.t()} | {:error, tuple} defp parse(input, options) when is_binary(input) do parse(%Language.Source{body: input}, options) end diff --git a/test/absinthe/integration/execution/token_limit_enforcement.exs b/test/absinthe/integration/execution/token_limit_enforcement.exs index 8b110d5d0d..a4d59facd2 100644 --- a/test/absinthe/integration/execution/token_limit_enforcement.exs +++ b/test/absinthe/integration/execution/token_limit_enforcement.exs @@ -1,13 +1,13 @@ defmodule Elixir.Absinthe.Integration.Execution.TokenLimitEnforcement do - use Absinthe.Case, async: true + use Absinthe.Case, async: true - @query """ - { - __typename @a @b @c @d @e - } - """ - test "Token limit lexer enforcement" do - assert {:ok, %{errors: [%{message: "Token limit exceeded"}]}} == - Absinthe.run(@query, Absinthe.Fixtures.Things.MacroSchema, [token_limit: 4]) - end + @query """ + { + __typename @a @b @c @d @e + } + """ + test "Token limit lexer enforcement" do + assert {:ok, %{errors: [%{message: "Token limit exceeded"}]}} == + Absinthe.run(@query, Absinthe.Fixtures.Things.MacroSchema, token_limit: 4) end +end diff --git a/test/absinthe/lexer_test.exs b/test/absinthe/lexer_test.exs index d27c363282..4b0a7f4ba6 100644 --- a/test/absinthe/lexer_test.exs +++ b/test/absinthe/lexer_test.exs @@ -105,8 +105,8 @@ defmodule Absinthe.LexerTest do end defp deep_query([]), do: "" + defp deep_query([field | rest]) do "{ #{field} #{deep_query(rest)} }" end - end From f40f68d39627608a4e56e035b455875670e8ddfd Mon Sep 17 00:00:00 2001 From: c-brenn Date: Thu, 1 Dec 2022 15:02:43 +0000 Subject: [PATCH 040/120] fixup! Allow `Registry` compression to be configured by users --- lib/absinthe/subscription.ex | 3 ++- lib/absinthe/subscription/supervisor.ex | 10 +++++++++- test/absinthe/execution/subscription_test.exs | 2 +- 3 files changed, 12 insertions(+), 3 deletions(-) diff --git a/lib/absinthe/subscription.ex b/lib/absinthe/subscription.ex index 09864ce08a..d35c4f4b28 100644 --- a/lib/absinthe/subscription.ex +++ b/lib/absinthe/subscription.ex @@ -35,7 +35,8 @@ defmodule Absinthe.Subscription do @doc """ Add Absinthe.Subscription to your process tree. """ - defdelegate start_link(opts), to: Subscription.Supervisor + @spec start_link(atom() | [opt()]) :: Supervisor.on_start() + defdelegate start_link(opts_or_pubsub), to: Subscription.Supervisor @type opt() :: {:pubsub, atom()} | {:compress_registry?, boolean()} | {:pool_size, pos_integer()} diff --git a/lib/absinthe/subscription/supervisor.ex b/lib/absinthe/subscription/supervisor.ex index fe0060c50e..7b63ae4ab9 100644 --- a/lib/absinthe/subscription/supervisor.ex +++ b/lib/absinthe/subscription/supervisor.ex @@ -3,7 +3,15 @@ defmodule Absinthe.Subscription.Supervisor do use Supervisor - def start_link(opts) do + @spec start_link(atom() | [Absinthe.Subscription.opt()]) :: Supervisor.on_start() + def start_link(pubsub) when is_atom(pubsub) do + # start_link/1 used to take a single argument - the pub-sub - so in order + # to maintain compatibility for existing users of the library we still + # accept this argument and transform it into a keyword list. + start_link(pubsub: pubsub) + end + + def start_link(opts) when is_list(opts) do pubsub = case Keyword.fetch!(opts, :pubsub) do [module] when is_atom(module) -> diff --git a/test/absinthe/execution/subscription_test.exs b/test/absinthe/execution/subscription_test.exs index a2f92efc6c..28233c0d4b 100644 --- a/test/absinthe/execution/subscription_test.exs +++ b/test/absinthe/execution/subscription_test.exs @@ -146,7 +146,7 @@ defmodule Absinthe.Execution.SubscriptionTest do setup_all do {:ok, _} = PubSub.start_link() - {:ok, _} = Absinthe.Subscription.start_link(pubsub: PubSub) + {:ok, _} = Absinthe.Subscription.start_link(PubSub) :ok end From c8d66428ace5016d6569ca85ab276ffb8fe3cc37 Mon Sep 17 00:00:00 2001 From: Ellie Poley Date: Fri, 2 Dec 2022 18:11:41 -0600 Subject: [PATCH 041/120] Implement the token enforcement using a short-circuiting approach in the lexer --- lib/absinthe/lexer.ex | 65 +++++++++++++++---- .../execution/token_limit_enforcement.exs | 42 ++++++++++-- 2 files changed, 87 insertions(+), 20 deletions(-) diff --git a/lib/absinthe/lexer.ex b/lib/absinthe/lexer.ex index 3caf46258a..177944e33f 100644 --- a/lib/absinthe/lexer.ex +++ b/lib/absinthe/lexer.ex @@ -42,6 +42,7 @@ defmodule Absinthe.Lexer do comment = string("#") |> repeat_while(any_unicode, {:not_line_terminator, []}) + |> post_traverse({:check_token_limit, []}) # Comma :: , comma = ascii_char([?,]) @@ -232,9 +233,15 @@ defmodule Absinthe.Lexer do def tokenize(input, options \\ []) do lines = String.split(input, ~r/\r?\n/) - case do_tokenize(input) do + tokenize_opts = [context: %{token_limit: Keyword.get(options, :token_limit, 15_000)}] + + case do_tokenize(input, tokenize_opts) do + {:error, :stopped_at_token_limit, _, _, _, _} -> + {:error, :exceeded_token_limit} + {:ok, tokens, "", _, _, _} -> - check_limit_and_process_tokens(tokens, options, lines) + tokens = Enum.map(tokens, &convert_token_column(&1, lines)) + {:ok, tokens} {:ok, _, rest, _, {line, line_offset}, byte_offset} -> byte_column = byte_offset - line_offset + 1 @@ -242,17 +249,6 @@ defmodule Absinthe.Lexer do end end - defp check_limit_and_process_tokens(tokens, options, lines) do - limit = Keyword.get(options, :token_limit, 15_000) - - if length(tokens) > limit do - {:error, :exceeded_token_limit} - else - tokens = Enum.map(tokens, &convert_token_column(&1, lines)) - {:ok, tokens} - end - end - defp convert_token_column({ident, loc, data}, lines) do {ident, byte_loc_to_char_loc(loc, lines), data} end @@ -321,7 +317,19 @@ defmodule Absinthe.Lexer do union ) |> Enum.map(&String.to_charlist/1) + defp boolean_value_or_name_or_reserved_word( + _, + _, + %{token_count: count, token_limit: limit} = _context, + _, + _ + ) + when count >= limit do + {:error, :stopped_at_token_limit} + end + defp boolean_value_or_name_or_reserved_word(rest, chars, context, loc, byte_offset) do + context = Map.update(context, :token_count, 1, &(&1 + 1)) value = chars |> Enum.reverse() do_boolean_value_or_name_or_reserved_word(rest, value, context, loc, byte_offset) end @@ -341,7 +349,12 @@ defmodule Absinthe.Lexer do {rest, [{:name, line_and_column(loc, byte_offset, length(value)), value}], context} end + defp labeled_token(_, _, %{token_count: count, token_limit: limit} = _context, _, _) + when count >= limit, + do: {:error, :stopped_at_token_limit} + defp labeled_token(rest, chars, context, loc, byte_offset, token_name) do + context = Map.update(context, :token_count, 1, &(&1 + 1)) value = chars |> Enum.reverse() {rest, [{token_name, line_and_column(loc, byte_offset, length(value)), value}], context} end @@ -354,24 +367,50 @@ defmodule Absinthe.Lexer do {rest, [], Map.put(context, :token_location, line_and_column(loc, byte_offset, 3))} end + defp block_string_value_token(_, _, %{token_count: count, token_limit: limit} = _context, _, _) + when count >= limit, + do: {:error, :stopped_at_token_limit} + defp block_string_value_token(rest, chars, context, _loc, _byte_offset) do + context = Map.update(context, :token_count, 1, &(&1 + 1)) value = '"""' ++ (chars |> Enum.reverse()) ++ '"""' {rest, [{:block_string_value, context.token_location, value}], Map.delete(context, :token_location)} end + defp string_value_token(_, _, %{token_count: count, token_limit: limit} = _context, _, _) + when count >= limit, + do: {:error, :stopped_at_token_limit} + defp string_value_token(rest, chars, context, _loc, _byte_offset) do + context = Map.update(context, :token_count, 1, &(&1 + 1)) value = '"' ++ tl(chars |> Enum.reverse()) ++ '"' {rest, [{:string_value, context.token_location, value}], Map.delete(context, :token_location)} end + defp atom_token(_, _, %{token_count: count, token_limit: limit} = _context, _, _) + when count >= limit do + {:error, :stopped_at_token_limit} + end + defp atom_token(rest, chars, context, loc, byte_offset) do + context = Map.update(context, :token_count, 1, &(&1 + 1)) value = chars |> Enum.reverse() token_atom = value |> List.to_atom() + {rest, [{token_atom, line_and_column(loc, byte_offset, length(value))}], context} end + defp check_token_limit(_, _, %{token_count: count, token_limit: limit} = _context, _, _) + when count >= limit, + do: {:error, :stopped_at_token_limit} + + defp check_token_limit(rest, chars, context, _loc, _byte_offset) do + context = Map.update(context, :token_count, 1, &(&1 + 1)) + {rest, [chars], context} + end + def line_and_column({line, line_offset}, byte_offset, column_correction) do column = byte_offset - line_offset - column_correction + 1 {line, column} diff --git a/test/absinthe/integration/execution/token_limit_enforcement.exs b/test/absinthe/integration/execution/token_limit_enforcement.exs index a4d59facd2..e3d8e9e02b 100644 --- a/test/absinthe/integration/execution/token_limit_enforcement.exs +++ b/test/absinthe/integration/execution/token_limit_enforcement.exs @@ -1,13 +1,41 @@ defmodule Elixir.Absinthe.Integration.Execution.TokenLimitEnforcement do use Absinthe.Case, async: true - @query """ - { - __typename @a @b @c @d @e - } - """ - test "Token limit lexer enforcement" do + test "Token limit lexer enforcement is precise" do + query = """ + { + __typename @a @b @c @d @e + } + """ + + assert {:ok, %{errors: [%{message: "Token limit exceeded"}]}} == + Absinthe.run(query, Absinthe.Fixtures.Things.MacroSchema, token_limit: 12) + + refute {:ok, %{errors: [%{message: "Token limit exceeded"}]}} == + Absinthe.run(query, Absinthe.Fixtures.Things.MacroSchema, token_limit: 13) + + query = """ + { + test(arg1: false, arg2: ["hi \u1F600", "hello", null], arg3: 3.14) { + results { + id #it's a guid + name @fakedirective + + ... on SomeType { + some\u0046ield #should expand to someField without an extra token + } + } + } + } + """ + + # token count 33 = 8 braces + 2 parens + 2 brackets + 2 string values + 1 null + 1 float + 1 bool + + # 3 colons + 1 ... + 1 on + 0 ignroed comment + 1@ + 1 directive + 9 names + assert {:ok, %{errors: [%{message: "Token limit exceeded"}]}} == - Absinthe.run(@query, Absinthe.Fixtures.Things.MacroSchema, token_limit: 4) + Absinthe.run(query, Absinthe.Fixtures.Things.MacroSchema, token_limit: 32) + + refute {:ok, %{errors: [%{message: "Token limit exceeded"}]}} == + Absinthe.run(query, Absinthe.Fixtures.Things.MacroSchema, token_limit: 33) end end From 594f4f3b0a6f066672853459ef470e9033d3d154 Mon Sep 17 00:00:00 2001 From: Ellie Poley Date: Fri, 2 Dec 2022 18:12:52 -0600 Subject: [PATCH 042/120] I may be wrong but it seems to me like shouldn't be here since it's in --- lib/absinthe/lexer.ex | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/lib/absinthe/lexer.ex b/lib/absinthe/lexer.ex index 177944e33f..76ffe2cfdb 100644 --- a/lib/absinthe/lexer.ex +++ b/lib/absinthe/lexer.ex @@ -42,7 +42,6 @@ defmodule Absinthe.Lexer do comment = string("#") |> repeat_while(any_unicode, {:not_line_terminator, []}) - |> post_traverse({:check_token_limit, []}) # Comma :: , comma = ascii_char([?,]) @@ -271,7 +270,6 @@ defmodule Absinthe.Lexer do repeat( choice([ ignore(ignored), - comment, punctuator, block_string_value, string_value, @@ -402,15 +400,6 @@ defmodule Absinthe.Lexer do {rest, [{token_atom, line_and_column(loc, byte_offset, length(value))}], context} end - defp check_token_limit(_, _, %{token_count: count, token_limit: limit} = _context, _, _) - when count >= limit, - do: {:error, :stopped_at_token_limit} - - defp check_token_limit(rest, chars, context, _loc, _byte_offset) do - context = Map.update(context, :token_count, 1, &(&1 + 1)) - {rest, [chars], context} - end - def line_and_column({line, line_offset}, byte_offset, column_correction) do column = byte_offset - line_offset - column_correction + 1 {line, column} From 0ff0fcdf452d2330d916f2d963c1a13b6af20691 Mon Sep 17 00:00:00 2001 From: Anton Satin Date: Sun, 4 Dec 2022 11:48:02 +0200 Subject: [PATCH 043/120] Pass resolution to dataloader callback Allows to get resolution structure from dataloader callback function. Adds support for callback with arity 4, in that case last argument will be resolution struct. --- lib/absinthe/resolution/helpers.ex | 33 ++++++++---- test/absinthe/middleware/dataloader_test.exs | 54 ++++++++++++++++++++ 2 files changed, 77 insertions(+), 10 deletions(-) diff --git a/lib/absinthe/resolution/helpers.ex b/lib/absinthe/resolution/helpers.ex index 94a42e19de..5fe05f166b 100644 --- a/lib/absinthe/resolution/helpers.ex +++ b/lib/absinthe/resolution/helpers.ex @@ -132,7 +132,9 @@ defmodule Absinthe.Resolution.Helpers do @type dataloader_opt :: {:args, map} | {:use_parent, true | false} - | {:callback, (map(), map(), map() -> any())} + | {:callback, + (map(), map(), map() -> any()) + | (map(), map(), map(), Absinthe.Resolution.t() -> any())} @doc """ Resolve a field with a dataloader source. @@ -200,7 +202,7 @@ defmodule Absinthe.Resolution.Helpers do def dataloader(source, opts) when is_list(opts) do fn parent, args, %{context: %{loader: loader}} = res -> resource = res.definition.schema_node.identifier - do_dataloader(loader, source, {resource, args}, parent, opts) + do_dataloader(loader, source, {resource, args}, parent, res, opts) end end @@ -270,7 +272,8 @@ defmodule Absinthe.Resolution.Helpers do in the event of a conflict, the resolver arguments win. - `:callback` default: return result wrapped in ok or error tuple. Callback that is run with result of dataloader. It receives the result as - the first argument, and the parent and args as second and third. Can be used + the first argument, and the parent and args as second and third. + Optionally can receive resolution as the fourth argument. Can be used to e.g. compute fields on the return value of the loader. Should return an ok or error tuple. - `:use_parent` default: `false`. This option affects whether or not the `dataloader/2` @@ -310,13 +313,13 @@ defmodule Absinthe.Resolution.Helpers do %{batch: batch, item: item} -> {batch, item} end - do_dataloader(loader, source, batch_key, parent, opts) + do_dataloader(loader, source, batch_key, parent, res, opts) end end def dataloader(source, resource, opts) do - fn parent, args, %{context: %{loader: loader}} -> - do_dataloader(loader, source, {resource, args}, parent, opts) + fn parent, args, %{context: %{loader: loader}} = res -> + do_dataloader(loader, source, {resource, args}, parent, res, opts) end end @@ -337,7 +340,7 @@ defmodule Absinthe.Resolution.Helpers do defp use_parent(loader, _source, _batch_key, _parent, _opts), do: loader - defp do_dataloader(loader, source, batch_key, parent, opts) do + defp do_dataloader(loader, source, batch_key, parent, res, opts) do args_from_opts = Keyword.get(opts, :args, %{}) {batch_key, args} = @@ -357,9 +360,19 @@ defmodule Absinthe.Resolution.Helpers do |> on_load(fn loader -> callback = Keyword.get(opts, :callback, default_callback(loader)) - loader - |> Dataloader.get(source, batch_key, parent) - |> callback.(parent, args) + item = Dataloader.get(loader, source, batch_key, parent) + + case callback do + callback when is_function(callback, 3) -> + callback.(item, parent, args) + + callback when is_function(callback, 4) -> + callback.(item, parent, args, res) + + callback -> + raise ArgumentError, + "Callback must be a function with arity either 3 or 4, got: #{inspect(callback)}" + end end) end diff --git a/test/absinthe/middleware/dataloader_test.exs b/test/absinthe/middleware/dataloader_test.exs index 201cd1bfcf..0c82a7cdc8 100644 --- a/test/absinthe/middleware/dataloader_test.exs +++ b/test/absinthe/middleware/dataloader_test.exs @@ -77,6 +77,26 @@ defmodule Absinthe.Middleware.DataloaderTest do field :bar_organization, :organization do resolve dataloader(:test, :organization, args: %{pid: self()}, use_parent: true) end + + field :bar_organization_name, :string do + resolve dataloader( + :test, + :organization, + args: %{pid: self()}, + callback: fn organization, _parent, _args -> + {:ok, organization.name} + end + ) + end + + field :bar_organization_state, :string do + resolve dataloader(:test, :organization, + args: %{pid: self()}, + callback: fn organization, _parent, _args, resolution -> + {:ok, "#{organization.name} - #{resolution.state}"} + end + ) + end end query do @@ -165,6 +185,40 @@ defmodule Absinthe.Middleware.DataloaderTest do refute_receive(:loading) end + test "can resolve fields using dataloader helper with callback" do + doc = """ + { + users { + organizationName: barOrganizationName + organizationState: barOrganizationState + } + } + """ + + expected_data = %{ + "users" => [ + %{ + "organizationName" => "Organization: #1", + "organizationState" => "Organization: #1 - unresolved" + }, + %{ + "organizationName" => "Organization: #2", + "organizationState" => "Organization: #2 - unresolved" + }, + %{ + "organizationName" => "Organization: #3", + "organizationState" => "Organization: #3 - unresolved" + } + ] + } + + assert {:ok, %{data: data}} = Absinthe.run(doc, DefaultSchema) + assert expected_data == data + + assert_receive(:loading) + refute_receive(:loading) + end + test "can resolve a field when dataloader uses 'tuples' get_policy" do doc = """ { From 6aa233b065b3d1f6902a0e2cb35fb3d11fc38450 Mon Sep 17 00:00:00 2001 From: Ben Wilson Date: Sun, 4 Dec 2022 11:49:42 -0500 Subject: [PATCH 044/120] changelog update --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index a226cbf679..b4f868c98e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,7 @@ - Bug Fix: Adds **optional fix** for non compliant built-in scalar Int type. `use Absinthe.Schema, use_spec_compliant_int_scalar: true` in your schema to use the fixed Int type. It is also advisable to upgrade for custom types if you are leveraging the use of integers outside the GraphQl standard. [#1131](https://github.com/absinthe-graphql/absinthe/pull/1131). - Feature: [Support error tuples when scalar parsing fails](https://github.com/absinthe-graphql/absinthe/pull/1187) - Feature: [Convert SDL Language.\* structs to SDL notation](https://github.com/absinthe-graphql/absinthe/pull/1160) +- Feature: [Support passing the resolution struct to dataloader helper callbacks](https://github.com/absinthe-graphql/absinthe/pull/1211) - Feature: [Add support for type extensions](https://github.com/absinthe-graphql/absinthe/pull/1157) - Bug Fix: [Add type system directives to introspection results](https://github.com/absinthe-graphql/absinthe/pull/1189) - Bug Fix: [Add `__private__` field to EnumValueDefinition](https://github.com/absinthe-graphql/absinthe/pull/1148) From f3cec5ac6fbd955bc1c6d4b00dd6102bc2104c41 Mon Sep 17 00:00:00 2001 From: Ben Wilson Date: Sun, 4 Dec 2022 11:57:39 -0500 Subject: [PATCH 045/120] update to setup-beam for ci --- .github/workflows/elixir.yml | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/.github/workflows/elixir.yml b/.github/workflows/elixir.yml index c79946a104..a9350e64d0 100644 --- a/.github/workflows/elixir.yml +++ b/.github/workflows/elixir.yml @@ -14,19 +14,14 @@ jobs: strategy: matrix: elixir: - - "1.10" - - "1.11" - "1.12" - "1.13" + - "1.14" otp: - - "22" - "23" - "24" - exclude: - - elixir: "1.10" - otp: "24" include: - - elixir: "1.13" + - elixir: "1.14" otp: "24" format: true @@ -35,7 +30,7 @@ jobs: uses: actions/checkout@v2 - name: Set up Elixir - uses: erlef/setup-elixir@v1 + uses: erlef/setup-beam@v1 with: elixir-version: ${{ matrix.elixir }} otp-version: ${{ matrix.otp }} From 8b133572d59d753a135e2bd747c0d277aaa60a4c Mon Sep 17 00:00:00 2001 From: Ben Wilson Date: Sun, 4 Dec 2022 12:00:10 -0500 Subject: [PATCH 046/120] try an older ubuntu for broader OTP support --- .github/workflows/elixir.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/elixir.yml b/.github/workflows/elixir.yml index a9350e64d0..1d74473205 100644 --- a/.github/workflows/elixir.yml +++ b/.github/workflows/elixir.yml @@ -9,7 +9,7 @@ on: jobs: test: name: Elixir ${{matrix.elixir}} / OTP ${{matrix.otp}} - runs-on: ubuntu-latest + runs-on: ubuntu-18.04 strategy: matrix: From 4a59847af7022310d16a12c796645b3970dd5bbd Mon Sep 17 00:00:00 2001 From: Mortada AlKhars Date: Tue, 6 Dec 2022 19:22:01 +0300 Subject: [PATCH 047/120] allow changing result phase for subscriptions --- lib/absinthe/subscription/local.ex | 20 +++++- test/absinthe/execution/subscription_test.exs | 70 +++++++++++++++++++ 2 files changed, 89 insertions(+), 1 deletion(-) diff --git a/lib/absinthe/subscription/local.ex b/lib/absinthe/subscription/local.ex index 041896885c..b128bc4cf1 100644 --- a/lib/absinthe/subscription/local.ex +++ b/lib/absinthe/subscription/local.ex @@ -53,7 +53,7 @@ defmodule Absinthe.Subscription.Local do pipeline = [ pipeline, [ - Absinthe.Phase.Document.Result, + result_phase(doc), {Absinthe.Phase.Telemetry, event: [:subscription, :publish, :stop]} ] ] @@ -90,4 +90,22 @@ defmodule Absinthe.Subscription.Local do |> Enum.map(&to_string/1) |> Enum.flat_map(&Absinthe.Subscription.get(pubsub, {field, &1})) end + + defp result_phase(doc) do + # use the configured result phase from the initial pipeline + # this will allow the result of the subscription data to match + # the output of query/mutation. An example of result phase is + # Absinthe.Phoenix.Controller.Result where the output will have + # atom keys and allow struct to be returned + + doc.initial_phases + |> Pipeline.from(Phase.Blueprint) + |> case do + [{Phase.Blueprint, opts} | _] -> + Keyword.get(opts, :result_phase, Phase.Document.Result) + + _ -> + Phase.Document.Result + end + end end diff --git a/test/absinthe/execution/subscription_test.exs b/test/absinthe/execution/subscription_test.exs index 28233c0d4b..d7e2bdb00e 100644 --- a/test/absinthe/execution/subscription_test.exs +++ b/test/absinthe/execution/subscription_test.exs @@ -3,6 +3,48 @@ defmodule Absinthe.Execution.SubscriptionTest do import ExUnit.CaptureLog + defmodule ResultPase do + @moduledoc false + + alias Absinthe.Blueprint + use Absinthe.Phase + + def run(%Blueprint{} = bp, _options \\ []) do + result = Map.merge(bp.result, process(bp)) + {:ok, %{bp | result: result}} + end + + defp process(blueprint) do + data = data(blueprint.execution.result) + %{data: data} + end + + defp data(%{value: value}), do: value + + defp data(%{fields: []} = result) do + result.root_value + end + + defp data(%{fields: fields, emitter: emitter, root_value: root_value}) do + with %{put: _} <- emitter.flags, + true <- is_map(root_value) do + data = field_data(fields) + Map.merge(root_value, data) + else + _ -> + field_data(fields) + end + end + + defp field_data(fields, acc \\ []) + defp field_data([], acc), do: Map.new(acc) + + defp field_data([field | fields], acc) do + value = data(field) + field_data(fields, [{String.to_existing_atom(field.emitter.name), value} | acc]) + end + end + defmodule PubSub do @behaviour Absinthe.Subscription.Pubsub @@ -150,6 +192,34 @@ defmodule Absinthe.Execution.SubscriptionTest do :ok end + @query """ + subscription ($clientId: ID!) { + thing(clientId: $clientId) + } + """ + test "should use result_phase from main pipeline" do + client_id = "abc" + + assert {:ok, %{"subscribed" => topic}} = + run_subscription( + @query, + Schema, + variables: %{"clientId" => client_id}, + context: %{pubsub: PubSub}, + result_phase: ResultPase + ) + + Absinthe.Subscription.publish(PubSub, %{foo: "bar"}, thing: client_id) + + assert_receive({:broadcast, msg}) + + assert %{ + event: "subscription:data", + result: %{data: %{thing: %{foo: "bar"}}}, + topic: topic + } == msg + end + @query """ subscription ($clientId: ID!) { thing(clientId: $clientId) From 32db847a137834e618d6f66751b1fa3bcdc4a5fe Mon Sep 17 00:00:00 2001 From: Maarten van Vliet Date: Tue, 20 Dec 2022 14:03:36 +0100 Subject: [PATCH 048/120] Place extra errors in extensions field --- lib/absinthe/phase/document/result.ex | 39 +++++++++----- .../resolution/extra_error_fields_test.exs | 51 ++++++++++++++++++- 2 files changed, 76 insertions(+), 14 deletions(-) diff --git a/lib/absinthe/phase/document/result.ex b/lib/absinthe/phase/document/result.ex index b3e91f5c41..dd6a0928a8 100644 --- a/lib/absinthe/phase/document/result.ex +++ b/lib/absinthe/phase/document/result.ex @@ -7,12 +7,12 @@ defmodule Absinthe.Phase.Document.Result do use Absinthe.Phase @spec run(Blueprint.t() | Phase.Error.t(), Keyword.t()) :: {:ok, map} - def run(%Blueprint{} = bp, _options \\ []) do - result = Map.merge(bp.result, process(bp)) + def run(%Blueprint{} = bp, options \\ []) do + result = Map.merge(bp.result, process(bp, options)) {:ok, %{bp | result: result}} end - defp process(blueprint) do + defp process(blueprint, opts) do result = case blueprint.execution do %{validation_errors: [], result: nil} -> @@ -25,20 +25,20 @@ defmodule Absinthe.Phase.Document.Result do {:validation_failed, errors} end - format_result(result) + format_result(result, opts) end - defp format_result({:ok, {data, []}}) do + defp format_result({:ok, {data, []}}, _) do %{data: data} end - defp format_result({:ok, {data, errors}}) do - errors = errors |> Enum.uniq() |> Enum.map(&format_error/1) + defp format_result({:ok, {data, errors}}, opts) do + errors = errors |> Enum.uniq() |> Enum.map(&format_error(&1, opts)) %{data: data, errors: errors} end - defp format_result({:validation_failed, errors}) do - errors = errors |> Enum.uniq() |> Enum.map(&format_error/1) + defp format_result({:validation_failed, errors}, opts) do + errors = errors |> Enum.uniq() |> Enum.map(&format_error(&1, opts)) %{errors: errors} end @@ -109,12 +109,13 @@ defmodule Absinthe.Phase.Document.Result do defp field_name(%{alias: name}), do: name defp field_name(%{name: name}), do: name - defp format_error(%Phase.Error{locations: []} = error) do + defp format_error(%Phase.Error{locations: []} = error, opts) do error_object = %{message: error.message} - Map.merge(error.extra, error_object) + + merge_error_extensions(error_object, error.extra, opts) end - defp format_error(%Phase.Error{} = error) do + defp format_error(%Phase.Error{} = error, opts) do error_object = %{ message: error.message, locations: Enum.flat_map(error.locations, &format_location/1) @@ -126,7 +127,19 @@ defmodule Absinthe.Phase.Document.Result do path -> Map.put(error_object, :path, path) end - Map.merge(Map.new(error.extra), error_object) + merge_error_extensions(error_object, error.extra, opts) + end + + defp merge_error_extensions(error_object, extra, _opts) when extra == %{} do + error_object + end + + defp merge_error_extensions(error_object, extra, opts) do + if opts[:spec_compliant_errors] do + Map.merge(%{extensions: extra}, error_object) + else + Map.merge(extra, error_object) + end end defp format_location(%{line: line, column: col}) do diff --git a/test/absinthe/integration/execution/resolution/extra_error_fields_test.exs b/test/absinthe/integration/execution/resolution/extra_error_fields_test.exs index 11da0d00a5..a8d844a50b 100644 --- a/test/absinthe/integration/execution/resolution/extra_error_fields_test.exs +++ b/test/absinthe/integration/execution/resolution/extra_error_fields_test.exs @@ -1,11 +1,13 @@ defmodule Elixir.Absinthe.Integration.Execution.Resolution.ExtraErrorFieldsTest do use Absinthe.Case, async: true + alias Absinthe.Pipeline + alias Absinthe.Phase @query """ mutation { failingThing(type: WITH_CODE) { name } } """ - test "scenario #1" do + test "extra fields places in errors list" do assert {:ok, %{ data: %{"failingThing" => nil}, @@ -19,4 +21,51 @@ defmodule Elixir.Absinthe.Integration.Execution.Resolution.ExtraErrorFieldsTest ] }} == Absinthe.run(@query, Absinthe.Fixtures.Things.MacroSchema, []) end + + test "extra fields placed in extensions" do + pipeline = + Pipeline.for_document(Absinthe.Fixtures.Things.MacroSchema) + |> Pipeline.replace( + Phase.Document.Result, + {Phase.Document.Result, spec_compliant_errors: true} + ) + + assert {:ok, + %{ + result: %{ + data: %{"failingThing" => nil}, + errors: [ + %{ + message: "Custom Error", + path: ["failingThing"], + locations: [%{column: 12, line: 1}], + extensions: %{code: 42} + } + ] + } + }, _} = Pipeline.run(@query, pipeline) + end + + @query """ + mutation { failingThing(type: MULTIPLE) { name } } + """ + test "when no extra fields, extensions field is omitted" do + pipeline = + Pipeline.for_document(Absinthe.Fixtures.Things.MacroSchema) + |> Pipeline.replace( + Phase.Document.Result, + {Phase.Document.Result, spec_compliant_errors: true} + ) + + assert {:ok, + %{ + result: %{ + data: %{"failingThing" => nil}, + errors: [ + %{locations: [%{column: 12, line: 1}], message: "one", path: ["failingThing"]}, + %{locations: [%{column: 12, line: 1}], message: "two", path: ["failingThing"]} + ] + } + }, _} = Pipeline.run(@query, pipeline) + end end From 148d9ff1e64a6d826559447b1dabdc3a4e2b019c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Kowalski?= Date: Tue, 20 Dec 2022 15:18:11 +0100 Subject: [PATCH 049/120] Update introspection.md It looks like graphqlhub.com does not exist anymore. --- guides/introspection.md | 6 ------ 1 file changed, 6 deletions(-) diff --git a/guides/introspection.md b/guides/introspection.md index a61c935946..e4c59ee551 100644 --- a/guides/introspection.md +++ b/guides/introspection.md @@ -123,9 +123,3 @@ end If you'd prefer to use a desktop application, we recommend using the pre-built [Electron](https://electron.atom.io)-based wrapper application, [GraphiQL.app](https://github.com/skevy/graphiql-app). - -### GraphQL Hub - -[GraphQL Hub](https://www.graphqlhub.com/) is an interesting website that you -can use to introspect a number of public GraphQL servers, using GraphiQL in the -browser and providing useful examples. From 0995e4380877d5455db97766254edacf257b043f Mon Sep 17 00:00:00 2001 From: Ellie Poley Date: Wed, 28 Dec 2022 17:45:30 -0600 Subject: [PATCH 050/120] it works --- lib/absinthe/lexer.ex | 90 ++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 85 insertions(+), 5 deletions(-) diff --git a/lib/absinthe/lexer.ex b/lib/absinthe/lexer.ex index a481e53c12..c94575c546 100644 --- a/lib/absinthe/lexer.ex +++ b/lib/absinthe/lexer.ex @@ -233,7 +233,7 @@ defmodule Absinthe.Lexer do case do_tokenize(input) do {:ok, tokens, "", _, _, _} -> - tokens = Enum.map(tokens, &convert_token_column(&1, lines)) + tokens = optimized_map_token_column(tokens, lines) {:ok, tokens} {:ok, _, rest, _, {line, line_offset}, byte_offset} -> @@ -242,12 +242,92 @@ defmodule Absinthe.Lexer do end end - defp convert_token_column({ident, loc, data}, lines) do - {ident, byte_loc_to_char_loc(loc, lines), data} + defp optimized_map_token_column(tokens, [first_line | remaining_lines]) do + IO.inspect(tokens, label: "tokens") + + # lines and chars are 1 indexed not 0 indexed + do_optimized_map_token_column( + [], + tokens, + 1, + first_line, + remaining_lines, + 1, + 1 + ) + end + + defp do_optimized_map_token_column(results, [] = _tokens, _, _, _, _, _), do: results + + defp do_optimized_map_token_column( + results, + [current_token | rest_tokens], + line_num, + line_part, + remaining_lines, + char_offset, + byte_offset + ) do + # extract byte loc + {token_line_num, token_byte_col} = + case current_token do + {_, byte_location, _} -> byte_location + {_, byte_location} -> byte_location + end + |> IO.inspect(label: "token") + + # update the current line cursor if we need to move to the next line + {current_line_num, current_line, remaining_lines, char_offset, byte_offset} = + cond do + token_line_num > line_num -> + adjust_lines_cursor(remaining_lines, token_line_num, line_num) + |> IO.inspect(label: "gt") + + token_line_num == line_num -> + {line_num, line_part, remaining_lines, char_offset, byte_offset} + |> IO.inspect(label: "eq") + end + + adjusted_byte_col = token_byte_col - byte_offset + partial_byte_prefix = binary_part(current_line, 0, adjusted_byte_col) + char_col = String.length(partial_byte_prefix) + char_offset + + # byte_size is constant time!! https://hexdocs.pm/elixir/1.12/Kernel.html#byte_size/1 + next_line_part = + binary_part(current_line, adjusted_byte_col, byte_size(current_line) - adjusted_byte_col) + + next_byte_offset = token_byte_col + next_char_offset = char_col + + result = + case current_token do + {ident, _, data} -> {ident, {token_line_num, char_col}, data} + {ident, _} -> {ident, {token_line_num, char_col}} + end + + results = results ++ [result] + + IO.inspect("---") + + do_optimized_map_token_column( + results, + rest_tokens, + current_line_num, + next_line_part, + remaining_lines, + next_char_offset, + next_byte_offset + ) end - defp convert_token_column({ident, loc}, lines) do - {ident, byte_loc_to_char_loc(loc, lines)} + # refactor inline? + defp adjust_lines_cursor(lines, desired_line_num, current_line_num) do + IO.inspect(lines, label: "lines") + IO.inspect(desired_line_num, label: "desired") + IO.inspect(current_line_num, label: "current") + {_discarded, next_lines} = Enum.split(lines, desired_line_num - current_line_num - 1) + [current_line | remaining_lines] = next_lines + {desired_line_num, current_line, remaining_lines, 1, 1} end defp byte_loc_to_char_loc({line, byte_col}, lines) do From 8a591af8a39289c0065bf635a542aa79bfec943e Mon Sep 17 00:00:00 2001 From: Ellie Poley Date: Wed, 28 Dec 2022 17:47:22 -0600 Subject: [PATCH 051/120] comment out inspects --- lib/absinthe/lexer.ex | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/lib/absinthe/lexer.ex b/lib/absinthe/lexer.ex index c94575c546..96a79a0931 100644 --- a/lib/absinthe/lexer.ex +++ b/lib/absinthe/lexer.ex @@ -274,18 +274,20 @@ defmodule Absinthe.Lexer do {_, byte_location, _} -> byte_location {_, byte_location} -> byte_location end - |> IO.inspect(label: "token") + + # |> IO.inspect(label: "token") # update the current line cursor if we need to move to the next line {current_line_num, current_line, remaining_lines, char_offset, byte_offset} = cond do token_line_num > line_num -> adjust_lines_cursor(remaining_lines, token_line_num, line_num) - |> IO.inspect(label: "gt") + + # |> IO.inspect(label: "gt") token_line_num == line_num -> {line_num, line_part, remaining_lines, char_offset, byte_offset} - |> IO.inspect(label: "eq") + # |> IO.inspect(label: "eq") end adjusted_byte_col = token_byte_col - byte_offset @@ -307,7 +309,7 @@ defmodule Absinthe.Lexer do results = results ++ [result] - IO.inspect("---") + # IO.inspect("---") do_optimized_map_token_column( results, @@ -322,9 +324,9 @@ defmodule Absinthe.Lexer do # refactor inline? defp adjust_lines_cursor(lines, desired_line_num, current_line_num) do - IO.inspect(lines, label: "lines") - IO.inspect(desired_line_num, label: "desired") - IO.inspect(current_line_num, label: "current") + # IO.inspect(lines, label: "lines") + # IO.inspect(desired_line_num, label: "desired") + # IO.inspect(current_line_num, label: "current") {_discarded, next_lines} = Enum.split(lines, desired_line_num - current_line_num - 1) [current_line | remaining_lines] = next_lines {desired_line_num, current_line, remaining_lines, 1, 1} From b4c35efbdad17a007f97db8029d51fafb8386866 Mon Sep 17 00:00:00 2001 From: Ellie Poley Date: Wed, 28 Dec 2022 17:51:50 -0600 Subject: [PATCH 052/120] one more to comment out --- lib/absinthe/lexer.ex | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/absinthe/lexer.ex b/lib/absinthe/lexer.ex index 96a79a0931..7311d2359c 100644 --- a/lib/absinthe/lexer.ex +++ b/lib/absinthe/lexer.ex @@ -243,7 +243,7 @@ defmodule Absinthe.Lexer do end defp optimized_map_token_column(tokens, [first_line | remaining_lines]) do - IO.inspect(tokens, label: "tokens") + # IO.inspect(tokens, label: "tokens") # lines and chars are 1 indexed not 0 indexed do_optimized_map_token_column( From 1bbe533e5c573b319ded9fe426dd32384df73838 Mon Sep 17 00:00:00 2001 From: Ellie Poley Date: Thu, 29 Dec 2022 11:46:42 -0600 Subject: [PATCH 053/120] perf test --- test/absinthe/lexer_test.exs | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/test/absinthe/lexer_test.exs b/test/absinthe/lexer_test.exs index a56f4c0015..045aa6d2ec 100644 --- a/test/absinthe/lexer_test.exs +++ b/test/absinthe/lexer_test.exs @@ -94,4 +94,13 @@ defmodule Absinthe.LexerTest do {:"}", {7, 1}} ]} == Absinthe.Lexer.tokenize(@query) end + + @query """ + { test1 { test2 test3 test4 test5 test6 test7 test8 test9 test10 test11 test12 test13 test14 test15 test16 test17 test18 test19 test20 test21 test22 test23 test24 test25 test26 test27 test28 test29 test30 test31 test32 test33 test34 test35 test36 test37 test38 test39 test40 test41 test42 test43 test44 test45 test46 test47 test48 test49 test50 test51 test52 test53 test54 test55 test56 test57 test58 test59 test60 test61 test62 test63 test64 test65 test66 test67 test68 test69 test70 test71 test72 test73 test74 test75 test76 test77 test78 test79 test80 test81 test82 test83 test84 test85 test86 test87 test88 test89 test90 test91 test92 test93 test94 test95 test96 test97 test98 test99 test100 test101 test102 test103 test104 test105 test106 test107 test108 test109 test110 test111 test112 test113 test114 test115 test116 test117 test118 test119 test120 test121 test122 test123 test124 test125 test126 test127 test128 test129 test130 test131 test132 test133 test134 test135 test136 test137 test138 test139 test140 test141 test142 test143 test144 test145 test146 test147 test148 test149 test150 test151 test152 test153 test154 test155 test156 test157 test158 test159 test160 test161 test162 test163 test164 test165 test166 test167 test168 test169 test170 test171 test172 test173 test174 test175 test176 test177 test178 test179 test180 test181 test182 test183 test184 test185 test186 test187 test188 test189 test190 test191 test192 test193 test194 test195 test196 test197 test198 test199 test200 test201 test202 test203 test204 test205 test206 test207 test208 test209 test210 test211 test212 test213 test214 test215 test216 test217 test218 test219 test220 test221 test222 test223 test224 test225 test226 test227 test228 test229 test230 test231 test232 test233 test234 test235 test236 test237 test238 test239 test240 test241 test242 test243 test244 test245 test246 test247 test248 test249 test250 test251 test252 test253 test254 test255 test256 test257 test258 test259 test260 test261 test262 test263 test264 test265 test266 test267 test268 test269 test270 test271 test272 test273 test274 test275 test276 test277 test278 test279 test280 test281 test282 test283 test284 test285 test286 test287 test288 test289 test290 test291 test292 test293 test294 test295 test296 test297 test298 test299 test300 test301 test302 test303 test304 test305 test306 test307 test308 test309 test310 test311 test312 test313 test314 test315 test316 test317 test318 test319 test320 test321 test322 test323 test324 test325 test326 test327 test328 test329 test330 test331 test332 test333 test334 test335 test336 test337 test338 test339 test340 test341 test342 test343 test344 test345 test346 test347 test348 test349 test350 test351 test352 test353 test354 test355 test356 test357 test358 test359 test360 test361 test362 test363 test364 test365 test366 test367 test368 test369 test370 test371 test372 test373 test374 test375 test376 test377 test378 test379 test380 test381 test382 test383 test384 test385 test386 test387 test388 test389 test390 test391 test392 test393 test394 test395 test396 test397 test398 test399 test400 test401 test402 test403 test404 test405 test406 test407 test408 test409 test410 test411 test412 test413 test414 test415 test416 test417 test418 test419 test420 test421 test422 test423 test424 test425 test426 test427 test428 test429 test430 test431 test432 test433 test434 test435 test436 test437 test438 test439 test440 test441 test442 test443 test444 test445 test446 test447 test448 test449 test450 test451 test452 test453 test454 test455 test456 test457 test458 test459 test460 test461 test462 test463 test464 test465 test466 test467 test468 test469 test470 test471 test472 test473 test474 test475 test476 test477 test478 test479 test480 test481 test482 test483 test484 test485 test486 test487 test488 test489 test490 test491 test492 test493 test494 test495 test496 test497 test498 test499 test500 test501 test502 test503 test504 test505 test506 test507 test508 test509 test510 test511 test512 test513 test514 test515 test516 test517 test518 test519 test520 test521 test522 test523 test524 test525 test526 test527 test528 test529 test530 test531 test532 test533 test534 test535 test536 test537 test538 test539 test540 test541 test542 test543 test544 test545 test546 test547 test548 test549 test550 test551 test552 test553 test554 test555 test556 test557 test558 test559 test560 test561 test562 test563 test564 test565 test566 test567 test568 test569 test570 test571 test572 test573 test574 test575 test576 test577 test578 test579 test580 test581 test582 test583 test584 test585 test586 test587 test588 test589 test590 test591 test592 test593 test594 test595 test596 test597 test598 test599 test600 test601 test602 test603 test604 test605 test606 test607 test608 test609 test610 test611 test612 test613 test614 test615 test616 test617 test618 test619 test620 test621 test622 test623 test624 test625 test626 test627 test628 test629 test630 test631 test632 test633 test634 test635 test636 test637 test638 test639 test640 test641 test642 test643 test644 test645 test646 test647 test648 test649 test650 test651 test652 test653 test654 test655 test656 test657 test658 test659 test660 test661 test662 test663 test664 test665 test666 test667 test668 test669 test670 test671 test672 test673 test674 test675 test676 test677 test678 test679 test680 test681 test682 test683 test684 test685 test686 test687 test688 test689 test690 test691 test692 test693 test694 test695 test696 test697 test698 test699 test700 test701 test702 test703 test704 test705 test706 test707 test708 test709 test710 test711 test712 test713 test714 test715 test716 test717 test718 test719 test720 test721 test722 test723 test724 test725 test726 test727 test728 test729 test730 test731 test732 test733 test734 test735 test736 test737 test738 test739 test740 test741 test742 test743 test744 test745 test746 test747 test748 test749 test750 test751 test752 test753 test754 test755 test756 test757 test758 test759 test760 test761 test762 test763 test764 test765 test766 test767 test768 test769 test770 test771 test772 test773 test774 test775 test776 test777 test778 test779 test780 test781 test782 test783 test784 test785 test786 test787 test788 test789 test790 test791 test792 test793 test794 test795 test796 test797 test798 test799 test800 test801 test802 test803 test804 test805 test806 test807 test808 test809 test810 test811 test812 test813 test814 test815 test816 test817 test818 test819 test820 test821 test822 test823 test824 test825 test826 test827 test828 test829 test830 test831 test832 test833 test834 test835 test836 test837 test838 test839 test840 test841 test842 test843 test844 test845 test846 test847 test848 test849 test850 test851 test852 test853 test854 test855 test856 test857 test858 test859 test860 test861 test862 test863 test864 test865 test866 test867 test868 test869 test870 test871 test872 test873 test874 test875 test876 test877 test878 test879 test880 test881 test882 test883 test884 test885 test886 test887 test888 test889 test890 test891 test892 test893 test894 test895 test896 test897 test898 test899 test900 test901 test902 test903 test904 test905 test906 test907 test908 test909 test910 test911 test912 test913 test914 test915 test916 test917 test918 test919 test920 test921 test922 test923 test924 test925 test926 test927 test928 test929 test930 test931 test932 test933 test934 test935 test936 test937 test938 test939 test940 test941 test942 test943 test944 test945 test946 test947 test948 test949 test950 test951 test952 test953 test954 test955 test956 test957 test958 test959 test960 test961 test962 test963 test964 test965 test966 test967 test968 test969 test970 test971 test972 test973 test974 test975 test976 test977 test978 test979 test980 test981 test982 test983 test984 test985 test986 test987 test988 test989 test990 test991 test992 test993 test994 test995 test996 test997 test998 test999 test1000 test1001 test1002 test1003 test1004 test1005 test1006 test1007 test1008 test1009 test1010 test1011 test1012 test1013 test1014 test1015 test1016 test1017 test1018 test1019 test1020 test1021 test1022 test1023 test1024 test1025 test1026 test1027 test1028 test1029 test1030 test1031 test1032 test1033 test1034 test1035 test1036 test1037 test1038 test1039 test1040 test1041 test1042 test1043 test1044 test1045 test1046 test1047 test1048 test1049 test1050 test1051 test1052 test1053 test1054 test1055 test1056 test1057 test1058 test1059 test1060 test1061 test1062 test1063 test1064 test1065 test1066 test1067 test1068 test1069 test1070 test1071 test1072 test1073 test1074 test1075 test1076 test1077 test1078 test1079 test1080 test1081 test1082 test1083 test1084 test1085 test1086 test1087 test1088 test1089 test1090 test1091 test1092 test1093 test1094 test1095 test1096 test1097 test1098 test1099 test1100 test1101 test1102 test1103 test1104 test1105 test1106 test1107 test1108 test1109 test1110 test1111 test1112 test1113 test1114 test1115 test1116 test1117 test1118 test1119 test1120 test1121 test1122 test1123 test1124 test1125 test1126 test1127 test1128 test1129 test1130 test1131 test1132 test1133 test1134 test1135 test1136 test1137 test1138 test1139 test1140 test1141 test1142 test1143 test1144 test1145 test1146 test1147 test1148 test1149 test1150 test1151 test1152 test1153 test1154 test1155 test1156 test1157 test1158 test1159 test1160 test1161 test1162 test1163 test1164 test1165 test1166 test1167 test1168 test1169 test1170 test1171 test1172 test1173 test1174 test1175 test1176 test1177 test1178 test1179 test1180 test1181 test1182 test1183 test1184 test1185 test1186 test1187 test1188 test1189 test1190 test1191 test1192 test1193 test1194 test1195 test1196 test1197 test1198 test1199 test1200 test1201 test1202 test1203 test1204 test1205 test1206 test1207 test1208 test1209 test1210 test1211 test1212 test1213 test1214 test1215 test1216 test1217 test1218 test1219 test1220 test1221 test1222 test1223 test1224 test1225 test1226 test1227 test1228 test1229 test1230 test1231 test1232 test1233 test1234 test1235 test1236 test1237 test1238 test1239 test1240 test1241 test1242 test1243 test1244 test1245 test1246 test1247 test1248 test1249 test1250 test1251 test1252 test1253 test1254 test1255 test1256 test1257 test1258 test1259 test1260 test1261 test1262 test1263 test1264 test1265 test1266 test1267 test1268 test1269 test1270 test1271 test1272 test1273 test1274 test1275 test1276 test1277 test1278 test1279 test1280 test1281 test1282 test1283 test1284 test1285 test1286 test1287 test1288 test1289 test1290 test1291 test1292 test1293 test1294 test1295 test1296 test1297 test1298 test1299 test1300 test1301 test1302 test1303 test1304 test1305 test1306 test1307 test1308 test1309 test1310 test1311 test1312 test1313 test1314 test1315 test1316 test1317 test1318 test1319 test1320 test1321 test1322 test1323 test1324 test1325 test1326 test1327 test1328 test1329 test1330 test1331 test1332 test1333 test1334 test1335 test1336 test1337 test1338 test1339 test1340 test1341 test1342 test1343 test1344 test1345 test1346 test1347 test1348 test1349 test1350 test1351 test1352 test1353 test1354 test1355 test1356 test1357 test1358 test1359 test1360 test1361 test1362 test1363 test1364 test1365 test1366 test1367 test1368 test1369 test1370 test1371 test1372 test1373 test1374 test1375 test1376 test1377 test1378 test1379 test1380 test1381 test1382 test1383 test1384 test1385 test1386 test1387 test1388 test1389 test1390 test1391 test1392 test1393 test1394 test1395 test1396 test1397 test1398 test1399 test1400 test1401 test1402 test1403 test1404 test1405 test1406 test1407 test1408 test1409 test1410 test1411 test1412 test1413 test1414 test1415 test1416 test1417 test1418 test1419 test1420 test1421 test1422 test1423 test1424 test1425 test1426 test1427 test1428 test1429 test1430 test1431 test1432 test1433 test1434 test1435 test1436 test1437 test1438 test1439 test1440 test1441 test1442 test1443 test1444 test1445 test1446 test1447 test1448 test1449 test1450 test1451 test1452 test1453 test1454 test1455 test1456 test1457 test1458 test1459 test1460 test1461 test1462 test1463 test1464 test1465 test1466 test1467 test1468 test1469 test1470 test1471 test1472 test1473 test1474 test1475 test1476 test1477 test1478 test1479 test1480 test1481 test1482 test1483 test1484 test1485 test1486 test1487 test1488 test1489 test1490 test1491 test1492 test1493 test1494 test1495 test1496 test1497 test1498 test1499 test1500 test1501 test1502 test1503 test1504 test1505 test1506 test1507 test1508 test1509 test1510 test1511 test1512 test1513 test1514 test1515 test1516 test1517 test1518 test1519 test1520 test1521 test1522 test1523 test1524 test1525 test1526 test1527 test1528 test1529 test1530 test1531 test1532 test1533 test1534 test1535 test1536 test1537 test1538 test1539 test1540 test1541 test1542 test1543 test1544 test1545 test1546 test1547 test1548 test1549 test1550 test1551 test1552 test1553 test1554 test1555 test1556 test1557 test1558 test1559 test1560 test1561 test1562 test1563 test1564 test1565 test1566 test1567 test1568 test1569 test1570 test1571 test1572 test1573 test1574 test1575 test1576 test1577 test1578 test1579 test1580 test1581 test1582 test1583 test1584 test1585 test1586 test1587 test1588 test1589 test1590 test1591 test1592 test1593 test1594 test1595 test1596 test1597 test1598 test1599 test1600 test1601 test1602 test1603 test1604 test1605 test1606 test1607 test1608 test1609 test1610 test1611 test1612 test1613 test1614 test1615 test1616 test1617 test1618 test1619 test1620 test1621 test1622 test1623 test1624 test1625 test1626 test1627 test1628 test1629 test1630 test1631 test1632 test1633 test1634 test1635 test1636 test1637 test1638 test1639 test1640 test1641 test1642 test1643 test1644 test1645 test1646 test1647 test1648 test1649 test1650 test1651 test1652 test1653 test1654 test1655 test1656 test1657 test1658 test1659 test1660 test1661 test1662 test1663 test1664 test1665 test1666 test1667 test1668 test1669 test1670 test1671 test1672 test1673 test1674 test1675 test1676 test1677 test1678 test1679 test1680 test1681 test1682 test1683 test1684 test1685 test1686 test1687 test1688 test1689 test1690 test1691 test1692 test1693 test1694 test1695 test1696 test1697 test1698 test1699 test1700 test1701 test1702 test1703 test1704 test1705 test1706 test1707 test1708 test1709 test1710 test1711 test1712 test1713 test1714 test1715 test1716 test1717 test1718 test1719 test1720 test1721 test1722 test1723 test1724 test1725 test1726 test1727 test1728 test1729 test1730 test1731 test1732 test1733 test1734 test1735 test1736 test1737 test1738 test1739 test1740 test1741 test1742 test1743 test1744 test1745 test1746 test1747 test1748 test1749 test1750 test1751 test1752 test1753 test1754 test1755 test1756 test1757 test1758 test1759 test1760 test1761 test1762 test1763 test1764 test1765 test1766 test1767 test1768 test1769 test1770 test1771 test1772 test1773 test1774 test1775 test1776 test1777 test1778 test1779 test1780 test1781 test1782 test1783 test1784 test1785 test1786 test1787 test1788 test1789 test1790 test1791 test1792 test1793 test1794 test1795 test1796 test1797 test1798 test1799 test1800 test1801 test1802 test1803 test1804 test1805 test1806 test1807 test1808 test1809 test1810 test1811 test1812 test1813 test1814 test1815 test1816 test1817 test1818 test1819 test1820 test1821 test1822 test1823 test1824 test1825 test1826 test1827 test1828 test1829 test1830 test1831 test1832 test1833 test1834 test1835 test1836 test1837 test1838 test1839 test1840 test1841 test1842 test1843 test1844 test1845 test1846 test1847 test1848 test1849 test1850 test1851 test1852 test1853 test1854 test1855 test1856 test1857 test1858 test1859 test1860 test1861 test1862 test1863 test1864 test1865 test1866 test1867 test1868 test1869 test1870 test1871 test1872 test1873 test1874 test1875 test1876 test1877 test1878 test1879 test1880 test1881 test1882 test1883 test1884 test1885 test1886 test1887 test1888 test1889 test1890 test1891 test1892 test1893 test1894 test1895 test1896 test1897 test1898 test1899 test1900 test1901 test1902 test1903 test1904 test1905 test1906 test1907 test1908 test1909 test1910 test1911 test1912 test1913 test1914 test1915 test1916 test1917 test1918 test1919 test1920 test1921 test1922 test1923 test1924 test1925 test1926 test1927 test1928 test1929 test1930 test1931 test1932 test1933 test1934 test1935 test1936 test1937 test1938 test1939 test1940 test1941 test1942 test1943 test1944 test1945 test1946 test1947 test1948 test1949 test1950 test1951 test1952 test1953 test1954 test1955 test1956 test1957 test1958 test1959 test1960 test1961 test1962 test1963 test1964 test1965 test1966 test1967 test1968 test1969 test1970 test1971 test1972 test1973 test1974 test1975 test1976 test1977 test1978 test1979 test1980 test1981 test1982 test1983 test1984 test1985 test1986 test1987 test1988 test1989 test1990 test1991 test1992 test1993 test1994 test1995 test1996 test1997 test1998 test1999 test2000 test2001 test2002 test2003 test2004 test2005 test2006 test2007 test2008 test2009 test2010 test2011 test2012 test2013 test2014 test2015 test2016 test2017 test2018 test2019 test2020 test2021 test2022 test2023 test2024 test2025 test2026 test2027 test2028 test2029 test2030 test2031 test2032 test2033 test2034 test2035 test2036 test2037 test2038 test2039 test2040 test2041 test2042 test2043 test2044 test2045 test2046 test2047 test2048 test2049 test2050 test2051 test2052 test2053 test2054 test2055 test2056 test2057 test2058 test2059 test2060 test2061 test2062 test2063 test2064 test2065 test2066 test2067 test2068 test2069 test2070 test2071 test2072 test2073 test2074 test2075 test2076 test2077 test2078 test2079 test2080 test2081 test2082 test2083 test2084 test2085 test2086 test2087 test2088 test2089 test2090 test2091 test2092 test2093 test2094 test2095 test2096 test2097 test2098 test2099 test2100 test2101 test2102 test2103 test2104 test2105 test2106 test2107 test2108 test2109 test2110 test2111 test2112 test2113 test2114 test2115 test2116 test2117 test2118 test2119 test2120 test2121 test2122 test2123 test2124 test2125 test2126 test2127 test2128 test2129 test2130 test2131 test2132 test2133 test2134 test2135 test2136 test2137 test2138 test2139 test2140 test2141 test2142 test2143 test2144 test2145 test2146 test2147 test2148 test2149 test2150 test2151 test2152 test2153 test2154 test2155 test2156 test2157 test2158 test2159 test2160 test2161 test2162 test2163 test2164 test2165 test2166 test2167 test2168 test2169 test2170 test2171 test2172 test2173 test2174 test2175 test2176 test2177 test2178 test2179 test2180 test2181 test2182 test2183 test2184 test2185 test2186 test2187 test2188 test2189 test2190 test2191 test2192 test2193 test2194 test2195 test2196 test2197 test2198 test2199 test2200 test2201 test2202 test2203 test2204 test2205 test2206 test2207 test2208 test2209 test2210 test2211 test2212 test2213 test2214 test2215 test2216 test2217 test2218 test2219 test2220 test2221 test2222 test2223 test2224 test2225 test2226 test2227 test2228 test2229 test2230 test2231 test2232 test2233 test2234 test2235 test2236 test2237 test2238 test2239 test2240 test2241 test2242 test2243 test2244 test2245 test2246 test2247 test2248 test2249 test2250 test2251 test2252 test2253 test2254 test2255 test2256 test2257 test2258 test2259 test2260 test2261 test2262 test2263 test2264 test2265 test2266 test2267 test2268 test2269 test2270 test2271 test2272 test2273 test2274 test2275 test2276 test2277 test2278 test2279 test2280 test2281 test2282 test2283 test2284 test2285 test2286 test2287 test2288 test2289 test2290 test2291 test2292 test2293 test2294 test2295 test2296 test2297 test2298 test2299 test2300 test2301 test2302 test2303 test2304 test2305 test2306 test2307 test2308 test2309 test2310 test2311 test2312 test2313 test2314 test2315 test2316 test2317 test2318 test2319 test2320 test2321 test2322 test2323 test2324 test2325 test2326 test2327 test2328 test2329 test2330 test2331 test2332 test2333 test2334 test2335 test2336 test2337 test2338 test2339 test2340 test2341 test2342 test2343 test2344 test2345 test2346 test2347 test2348 test2349 test2350 test2351 test2352 test2353 test2354 test2355 test2356 test2357 test2358 test2359 test2360 test2361 test2362 test2363 test2364 test2365 test2366 test2367 test2368 test2369 test2370 test2371 test2372 test2373 test2374 test2375 test2376 test2377 test2378 test2379 test2380 test2381 test2382 test2383 test2384 test2385 test2386 test2387 test2388 test2389 test2390 test2391 test2392 test2393 test2394 test2395 test2396 test2397 test2398 test2399 test2400 test2401 test2402 test2403 test2404 test2405 test2406 test2407 test2408 test2409 test2410 test2411 test2412 test2413 test2414 test2415 test2416 test2417 test2418 test2419 test2420 test2421 test2422 test2423 test2424 test2425 test2426 test2427 test2428 test2429 test2430 test2431 test2432 test2433 test2434 test2435 test2436 test2437 test2438 test2439 test2440 test2441 test2442 test2443 test2444 test2445 test2446 test2447 test2448 test2449 test2450 test2451 test2452 test2453 test2454 test2455 test2456 test2457 test2458 test2459 test2460 test2461 test2462 test2463 test2464 test2465 test2466 test2467 test2468 test2469 test2470 test2471 test2472 test2473 test2474 test2475 test2476 test2477 test2478 test2479 test2480 test2481 test2482 test2483 test2484 test2485 test2486 test2487 test2488 test2489 test2490 test2491 test2492 test2493 test2494 test2495 test2496 test2497 test2498 test2499 test2500 test2501 test2502 test2503 test2504 test2505 test2506 test2507 test2508 test2509 test2510 test2511 test2512 test2513 test2514 test2515 test2516 test2517 test2518 test2519 test2520 test2521 test2522 test2523 test2524 test2525 test2526 test2527 test2528 test2529 test2530 test2531 test2532 test2533 test2534 test2535 test2536 test2537 test2538 test2539 test2540 test2541 test2542 test2543 test2544 test2545 test2546 test2547 test2548 test2549 test2550 test2551 test2552 test2553 test2554 test2555 test2556 test2557 test2558 test2559 test2560 test2561 test2562 test2563 test2564 test2565 test2566 test2567 test2568 test2569 test2570 test2571 test2572 test2573 test2574 test2575 test2576 test2577 test2578 test2579 test2580 test2581 test2582 test2583 test2584 test2585 test2586 test2587 test2588 test2589 test2590 test2591 test2592 test2593 test2594 test2595 test2596 test2597 test2598 test2599 test2600 test2601 test2602 test2603 test2604 test2605 test2606 test2607 test2608 test2609 test2610 test2611 test2612 test2613 test2614 test2615 test2616 test2617 test2618 test2619 test2620 test2621 test2622 test2623 test2624 test2625 test2626 test2627 test2628 test2629 test2630 test2631 test2632 test2633 test2634 test2635 test2636 test2637 test2638 test2639 test2640 test2641 test2642 test2643 test2644 test2645 test2646 test2647 test2648 test2649 test2650 test2651 test2652 test2653 test2654 test2655 test2656 test2657 test2658 test2659 test2660 test2661 test2662 test2663 test2664 test2665 test2666 test2667 test2668 test2669 test2670 test2671 test2672 test2673 test2674 test2675 test2676 test2677 test2678 test2679 test2680 test2681 test2682 test2683 test2684 test2685 test2686 test2687 test2688 test2689 test2690 test2691 test2692 test2693 test2694 test2695 test2696 test2697 test2698 test2699 test2700 test2701 test2702 test2703 test2704 test2705 test2706 test2707 test2708 test2709 test2710 test2711 test2712 test2713 test2714 test2715 test2716 test2717 test2718 test2719 test2720 test2721 test2722 test2723 test2724 test2725 test2726 test2727 test2728 test2729 test2730 test2731 test2732 test2733 test2734 test2735 test2736 test2737 test2738 test2739 test2740 test2741 test2742 test2743 test2744 test2745 test2746 test2747 test2748 test2749 test2750 test2751 test2752 test2753 test2754 test2755 test2756 test2757 test2758 test2759 test2760 test2761 test2762 test2763 test2764 test2765 test2766 test2767 test2768 test2769 test2770 test2771 test2772 test2773 test2774 test2775 test2776 test2777 test2778 test2779 test2780 test2781 test2782 test2783 test2784 test2785 test2786 test2787 test2788 test2789 test2790 test2791 test2792 test2793 test2794 test2795 test2796 test2797 test2798 test2799 test2800 test2801 test2802 test2803 test2804 test2805 test2806 test2807 test2808 test2809 test2810 test2811 test2812 test2813 test2814 test2815 test2816 test2817 test2818 test2819 test2820 test2821 test2822 test2823 test2824 test2825 test2826 test2827 test2828 test2829 test2830 test2831 test2832 test2833 test2834 test2835 test2836 test2837 test2838 test2839 test2840 test2841 test2842 test2843 test2844 test2845 test2846 test2847 test2848 test2849 test2850 test2851 test2852 test2853 test2854 test2855 test2856 test2857 test2858 test2859 test2860 test2861 test2862 test2863 test2864 test2865 test2866 test2867 test2868 test2869 test2870 test2871 test2872 test2873 test2874 test2875 test2876 test2877 test2878 test2879 test2880 test2881 test2882 test2883 test2884 test2885 test2886 test2887 test2888 test2889 test2890 test2891 test2892 test2893 test2894 test2895 test2896 test2897 test2898 test2899 test2900 test2901 test2902 test2903 test2904 test2905 test2906 test2907 test2908 test2909 test2910 test2911 test2912 test2913 test2914 test2915 test2916 test2917 test2918 test2919 test2920 test2921 test2922 test2923 test2924 test2925 test2926 test2927 test2928 test2929 test2930 test2931 test2932 test2933 test2934 test2935 test2936 test2937 test2938 test2939 test2940 test2941 test2942 test2943 test2944 test2945 test2946 test2947 test2948 test2949 test2950 test2951 test2952 test2953 test2954 test2955 test2956 test2957 test2958 test2959 test2960 test2961 test2962 test2963 test2964 test2965 test2966 test2967 test2968 test2969 test2970 test2971 test2972 test2973 test2974 test2975 test2976 test2977 test2978 test2979 test2980 test2981 test2982 test2983 test2984 test2985 test2986 test2987 test2988 test2989 test2990 test2991 test2992 test2993 test2994 test2995 test2996 test2997 test2998 test2999 test3000 test3001 test3002 test3003 test3004 test3005 test3006 test3007 test3008 test3009 test3010 test3011 test3012 test3013 test3014 test3015 test3016 test3017 test3018 test3019 test3020 test3021 test3022 test3023 test3024 test3025 test3026 test3027 test3028 test3029 test3030 test3031 test3032 test3033 test3034 test3035 test3036 test3037 test3038 test3039 test3040 test3041 test3042 test3043 test3044 test3045 test3046 test3047 test3048 test3049 test3050 test3051 test3052 test3053 test3054 test3055 test3056 test3057 test3058 test3059 test3060 test3061 test3062 test3063 test3064 test3065 test3066 test3067 test3068 test3069 test3070 test3071 test3072 test3073 test3074 test3075 test3076 test3077 test3078 test3079 test3080 test3081 test3082 test3083 test3084 test3085 test3086 test3087 test3088 test3089 test3090 test3091 test3092 test3093 test3094 test3095 test3096 test3097 test3098 test3099 test3100 test3101 test3102 test3103 test3104 test3105 test3106 test3107 test3108 test3109 test3110 test3111 test3112 test3113 test3114 test3115 test3116 test3117 test3118 test3119 test3120 test3121 test3122 test3123 test3124 test3125 test3126 test3127 test3128 test3129 test3130 test3131 test3132 test3133 test3134 test3135 test3136 test3137 test3138 test3139 test3140 test3141 test3142 test3143 test3144 test3145 test3146 test3147 test3148 test3149 test3150 test3151 test3152 test3153 test3154 test3155 test3156 test3157 test3158 test3159 test3160 test3161 test3162 test3163 test3164 test3165 test3166 test3167 test3168 test3169 test3170 test3171 test3172 test3173 test3174 test3175 test3176 test3177 test3178 test3179 test3180 test3181 test3182 test3183 test3184 test3185 test3186 test3187 test3188 test3189 test3190 test3191 test3192 test3193 test3194 test3195 test3196 test3197 test3198 test3199 test3200 test3201 test3202 test3203 test3204 test3205 test3206 test3207 test3208 test3209 test3210 test3211 test3212 test3213 test3214 test3215 test3216 test3217 test3218 test3219 test3220 test3221 test3222 test3223 test3224 test3225 test3226 test3227 test3228 test3229 test3230 test3231 test3232 test3233 test3234 test3235 test3236 test3237 test3238 test3239 test3240 test3241 test3242 test3243 test3244 test3245 test3246 test3247 test3248 test3249 test3250 test3251 test3252 test3253 test3254 test3255 test3256 test3257 test3258 test3259 test3260 test3261 test3262 test3263 test3264 test3265 test3266 test3267 test3268 test3269 test3270 test3271 test3272 test3273 test3274 test3275 test3276 test3277 test3278 test3279 test3280 test3281 test3282 test3283 test3284 test3285 test3286 test3287 test3288 test3289 test3290 test3291 test3292 test3293 test3294 test3295 test3296 test3297 test3298 test3299 test3300 test3301 test3302 test3303 test3304 test3305 test3306 test3307 test3308 test3309 test3310 test3311 test3312 test3313 test3314 test3315 test3316 test3317 test3318 test3319 test3320 test3321 test3322 test3323 test3324 test3325 test3326 test3327 test3328 test3329 test3330 test3331 test3332 test3333 test3334 test3335 test3336 test3337 test3338 test3339 test3340 test3341 test3342 test3343 test3344 test3345 test3346 test3347 test3348 test3349 test3350 test3351 test3352 test3353 test3354 test3355 test3356 test3357 test3358 test3359 test3360 test3361 test3362 test3363 test3364 test3365 test3366 test3367 test3368 test3369 test3370 test3371 test3372 test3373 test3374 test3375 test3376 test3377 test3378 test3379 test3380 test3381 test3382 test3383 test3384 test3385 test3386 test3387 test3388 test3389 test3390 test3391 test3392 test3393 test3394 test3395 test3396 test3397 test3398 test3399 test3400 test3401 test3402 test3403 test3404 test3405 test3406 test3407 test3408 test3409 test3410 test3411 test3412 test3413 test3414 test3415 test3416 test3417 test3418 test3419 test3420 test3421 test3422 test3423 test3424 test3425 test3426 test3427 test3428 test3429 test3430 test3431 test3432 test3433 test3434 test3435 test3436 test3437 test3438 test3439 test3440 test3441 test3442 test3443 test3444 test3445 test3446 test3447 test3448 test3449 test3450 test3451 test3452 test3453 test3454 test3455 test3456 test3457 test3458 test3459 test3460 test3461 test3462 test3463 test3464 test3465 test3466 test3467 test3468 test3469 test3470 test3471 test3472 test3473 test3474 test3475 test3476 test3477 test3478 test3479 test3480 test3481 test3482 test3483 test3484 test3485 test3486 test3487 test3488 test3489 test3490 test3491 test3492 test3493 test3494 test3495 test3496 test3497 test3498 test3499 test3500 test3501 test3502 test3503 test3504 test3505 test3506 test3507 test3508 test3509 test3510 test3511 test3512 test3513 test3514 test3515 test3516 test3517 test3518 test3519 test3520 test3521 test3522 test3523 test3524 test3525 test3526 test3527 test3528 test3529 test3530 test3531 test3532 test3533 test3534 test3535 test3536 test3537 test3538 test3539 test3540 test3541 test3542 test3543 test3544 test3545 test3546 test3547 test3548 test3549 test3550 test3551 test3552 test3553 test3554 test3555 test3556 test3557 test3558 test3559 test3560 test3561 test3562 test3563 test3564 test3565 test3566 test3567 test3568 test3569 test3570 test3571 test3572 test3573 test3574 test3575 test3576 test3577 test3578 test3579 test3580 test3581 test3582 test3583 test3584 test3585 test3586 test3587 test3588 test3589 test3590 test3591 test3592 test3593 test3594 test3595 test3596 test3597 test3598 test3599 test3600 test3601 test3602 test3603 test3604 test3605 test3606 test3607 test3608 test3609 test3610 test3611 test3612 test3613 test3614 test3615 test3616 test3617 test3618 test3619 test3620 test3621 test3622 test3623 test3624 test3625 test3626 test3627 test3628 test3629 test3630 test3631 test3632 test3633 test3634 test3635 test3636 test3637 test3638 test3639 test3640 test3641 test3642 test3643 test3644 test3645 test3646 test3647 test3648 test3649 test3650 test3651 test3652 test3653 test3654 test3655 test3656 test3657 test3658 test3659 test3660 test3661 test3662 test3663 test3664 test3665 test3666 test3667 test3668 test3669 test3670 test3671 test3672 test3673 test3674 test3675 test3676 test3677 test3678 test3679 test3680 test3681 test3682 test3683 test3684 test3685 test3686 test3687 test3688 test3689 test3690 test3691 test3692 test3693 test3694 test3695 test3696 test3697 test3698 test3699 test3700 test3701 test3702 test3703 test3704 test3705 test3706 test3707 test3708 test3709 test3710 test3711 test3712 test3713 test3714 test3715 test3716 test3717 test3718 test3719 test3720 test3721 test3722 test3723 test3724 test3725 test3726 test3727 test3728 test3729 test3730 test3731 test3732 test3733 test3734 test3735 test3736 test3737 test3738 test3739 test3740 test3741 test3742 test3743 test3744 test3745 test3746 test3747 test3748 test3749 test3750 test3751 test3752 test3753 test3754 test3755 test3756 test3757 test3758 test3759 test3760 test3761 test3762 test3763 test3764 test3765 test3766 test3767 test3768 test3769 test3770 test3771 test3772 test3773 test3774 test3775 test3776 test3777 test3778 test3779 test3780 test3781 test3782 test3783 test3784 test3785 test3786 test3787 test3788 test3789 test3790 test3791 test3792 test3793 test3794 test3795 test3796 test3797 test3798 test3799 test3800 test3801 test3802 test3803 test3804 test3805 test3806 test3807 test3808 test3809 test3810 test3811 test3812 test3813 test3814 test3815 test3816 test3817 test3818 test3819 test3820 test3821 test3822 test3823 test3824 test3825 test3826 test3827 test3828 test3829 test3830 test3831 test3832 test3833 test3834 test3835 test3836 test3837 test3838 test3839 test3840 test3841 test3842 test3843 test3844 test3845 test3846 test3847 test3848 test3849 test3850 test3851 test3852 test3853 test3854 test3855 test3856 test3857 test3858 test3859 test3860 test3861 test3862 test3863 test3864 test3865 test3866 test3867 test3868 test3869 test3870 test3871 test3872 test3873 test3874 test3875 test3876 test3877 test3878 test3879 test3880 test3881 test3882 test3883 test3884 test3885 test3886 test3887 test3888 test3889 test3890 test3891 test3892 test3893 test3894 test3895 test3896 test3897 test3898 test3899 test3900 test3901 test3902 test3903 test3904 test3905 test3906 test3907 test3908 test3909 test3910 test3911 test3912 test3913 test3914 test3915 test3916 test3917 test3918 test3919 test3920 test3921 test3922 test3923 test3924 test3925 test3926 test3927 test3928 test3929 test3930 test3931 test3932 test3933 test3934 test3935 test3936 test3937 test3938 test3939 test3940 test3941 test3942 test3943 test3944 test3945 test3946 test3947 test3948 test3949 test3950 test3951 test3952 test3953 test3954 test3955 test3956 test3957 test3958 test3959 test3960 test3961 test3962 test3963 test3964 test3965 test3966 test3967 test3968 test3969 test3970 test3971 test3972 test3973 test3974 test3975 test3976 test3977 test3978 test3979 test3980 test3981 test3982 test3983 test3984 test3985 test3986 test3987 test3988 test3989 test3990 test3991 test3992 test3993 test3994 test3995 test3996 test3997 test3998 test3999 test4000 test4001 test4002 test4003 test4004 test4005 test4006 test4007 test4008 test4009 test4010 test4011 test4012 test4013 test4014 test4015 test4016 test4017 test4018 test4019 test4020 test4021 test4022 test4023 test4024 test4025 test4026 test4027 test4028 test4029 test4030 test4031 test4032 test4033 test4034 test4035 test4036 test4037 test4038 test4039 test4040 test4041 test4042 test4043 test4044 test4045 test4046 test4047 test4048 test4049 test4050 test4051 test4052 test4053 test4054 test4055 test4056 test4057 test4058 test4059 test4060 test4061 test4062 test4063 test4064 test4065 test4066 test4067 test4068 test4069 test4070 test4071 test4072 test4073 test4074 test4075 test4076 test4077 test4078 test4079 test4080 test4081 test4082 test4083 test4084 test4085 test4086 test4087 test4088 test4089 test4090 test4091 test4092 test4093 test4094 test4095 test4096 test4097 test4098 test4099 test4100 test4101 test4102 test4103 test4104 test4105 test4106 test4107 test4108 test4109 test4110 test4111 test4112 test4113 test4114 test4115 test4116 test4117 test4118 test4119 test4120 test4121 test4122 test4123 test4124 test4125 test4126 test4127 test4128 test4129 test4130 test4131 test4132 test4133 test4134 test4135 test4136 test4137 test4138 test4139 test4140 test4141 test4142 test4143 test4144 test4145 test4146 test4147 test4148 test4149 test4150 test4151 test4152 test4153 test4154 test4155 test4156 test4157 test4158 test4159 test4160 test4161 test4162 test4163 test4164 test4165 test4166 test4167 test4168 test4169 test4170 test4171 test4172 test4173 test4174 test4175 test4176 test4177 test4178 test4179 test4180 test4181 test4182 test4183 test4184 test4185 test4186 test4187 test4188 test4189 test4190 test4191 test4192 test4193 test4194 test4195 test4196 test4197 test4198 test4199 test4200 test4201 test4202 test4203 test4204 test4205 test4206 test4207 test4208 test4209 test4210 test4211 test4212 test4213 test4214 test4215 test4216 test4217 test4218 test4219 test4220 test4221 test4222 test4223 test4224 test4225 test4226 test4227 test4228 test4229 test4230 test4231 test4232 test4233 test4234 test4235 test4236 test4237 test4238 test4239 test4240 test4241 test4242 test4243 test4244 test4245 test4246 test4247 test4248 test4249 test4250 test4251 test4252 test4253 test4254 test4255 test4256 test4257 test4258 test4259 test4260 test4261 test4262 test4263 test4264 test4265 test4266 test4267 test4268 test4269 test4270 test4271 test4272 test4273 test4274 test4275 test4276 test4277 test4278 test4279 test4280 test4281 test4282 test4283 test4284 test4285 test4286 test4287 test4288 test4289 test4290 test4291 test4292 test4293 test4294 test4295 test4296 test4297 test4298 test4299 test4300 test4301 test4302 test4303 test4304 test4305 test4306 test4307 test4308 test4309 test4310 test4311 test4312 test4313 test4314 test4315 test4316 test4317 test4318 test4319 test4320 test4321 test4322 test4323 test4324 test4325 test4326 test4327 test4328 test4329 test4330 test4331 test4332 test4333 test4334 test4335 test4336 test4337 test4338 test4339 test4340 test4341 test4342 test4343 test4344 test4345 test4346 test4347 test4348 test4349 test4350 test4351 test4352 test4353 test4354 test4355 test4356 test4357 test4358 test4359 test4360 test4361 test4362 test4363 test4364 test4365 test4366 test4367 test4368 test4369 test4370 test4371 test4372 test4373 test4374 test4375 test4376 test4377 test4378 test4379 test4380 test4381 test4382 test4383 test4384 test4385 test4386 test4387 test4388 test4389 test4390 test4391 test4392 test4393 test4394 test4395 test4396 test4397 test4398 test4399 test4400 test4401 test4402 test4403 test4404 test4405 test4406 test4407 test4408 test4409 test4410 test4411 test4412 test4413 test4414 test4415 test4416 test4417 test4418 test4419 test4420 test4421 test4422 test4423 test4424 test4425 test4426 test4427 test4428 test4429 test4430 test4431 test4432 test4433 test4434 test4435 test4436 test4437 test4438 test4439 test4440 test4441 test4442 test4443 test4444 test4445 test4446 test4447 test4448 test4449 test4450 test4451 test4452 test4453 test4454 test4455 test4456 test4457 test4458 test4459 test4460 test4461 test4462 test4463 test4464 test4465 test4466 test4467 test4468 test4469 test4470 test4471 test4472 test4473 test4474 test4475 test4476 test4477 test4478 test4479 test4480 test4481 test4482 test4483 test4484 test4485 test4486 test4487 test4488 test4489 test4490 test4491 test4492 test4493 test4494 test4495 test4496 test4497 test4498 test4499 test4500 test4501 test4502 test4503 test4504 test4505 test4506 test4507 test4508 test4509 test4510 test4511 test4512 test4513 test4514 test4515 test4516 test4517 test4518 test4519 test4520 test4521 test4522 test4523 test4524 test4525 test4526 test4527 test4528 test4529 test4530 test4531 test4532 test4533 test4534 test4535 test4536 test4537 test4538 test4539 test4540 test4541 test4542 test4543 test4544 test4545 test4546 test4547 test4548 test4549 test4550 test4551 test4552 test4553 test4554 test4555 test4556 test4557 test4558 test4559 test4560 test4561 test4562 test4563 test4564 test4565 test4566 test4567 test4568 test4569 test4570 test4571 test4572 test4573 test4574 test4575 test4576 test4577 test4578 test4579 test4580 test4581 test4582 test4583 test4584 test4585 test4586 test4587 test4588 test4589 test4590 test4591 test4592 test4593 test4594 test4595 test4596 test4597 test4598 test4599 test4600 test4601 test4602 test4603 test4604 test4605 test4606 test4607 test4608 test4609 test4610 test4611 test4612 test4613 test4614 test4615 test4616 test4617 test4618 test4619 test4620 test4621 test4622 test4623 test4624 test4625 test4626 test4627 test4628 test4629 test4630 test4631 test4632 test4633 test4634 test4635 test4636 test4637 test4638 test4639 test4640 test4641 test4642 test4643 test4644 test4645 test4646 test4647 test4648 test4649 test4650 test4651 test4652 test4653 test4654 test4655 test4656 test4657 test4658 test4659 test4660 test4661 test4662 test4663 test4664 test4665 test4666 test4667 test4668 test4669 test4670 test4671 test4672 test4673 test4674 test4675 test4676 test4677 test4678 test4679 test4680 test4681 test4682 test4683 test4684 test4685 test4686 test4687 test4688 test4689 test4690 test4691 test4692 test4693 test4694 test4695 test4696 test4697 test4698 test4699 test4700 test4701 test4702 test4703 test4704 test4705 test4706 test4707 test4708 test4709 test4710 test4711 test4712 test4713 test4714 test4715 test4716 test4717 test4718 test4719 test4720 test4721 test4722 test4723 test4724 test4725 test4726 test4727 test4728 test4729 test4730 test4731 test4732 test4733 test4734 test4735 test4736 test4737 test4738 test4739 test4740 test4741 test4742 test4743 test4744 test4745 test4746 test4747 test4748 test4749 test4750 test4751 test4752 test4753 test4754 test4755 test4756 test4757 test4758 test4759 test4760 test4761 test4762 test4763 test4764 test4765 test4766 test4767 test4768 test4769 test4770 test4771 test4772 test4773 test4774 test4775 test4776 test4777 test4778 test4779 test4780 test4781 test4782 test4783 test4784 test4785 test4786 test4787 test4788 test4789 test4790 test4791 test4792 test4793 test4794 test4795 test4796 test4797 test4798 test4799 test4800 test4801 test4802 test4803 test4804 test4805 test4806 test4807 test4808 test4809 test4810 test4811 test4812 test4813 test4814 test4815 test4816 test4817 test4818 test4819 test4820 test4821 test4822 test4823 test4824 test4825 test4826 test4827 test4828 test4829 test4830 test4831 test4832 test4833 test4834 test4835 test4836 test4837 test4838 test4839 test4840 test4841 test4842 test4843 test4844 test4845 test4846 test4847 test4848 test4849 test4850 test4851 test4852 test4853 test4854 test4855 test4856 test4857 test4858 test4859 test4860 test4861 test4862 test4863 test4864 test4865 test4866 test4867 test4868 test4869 test4870 test4871 test4872 test4873 test4874 test4875 test4876 test4877 test4878 test4879 test4880 test4881 test4882 test4883 test4884 test4885 test4886 test4887 test4888 test4889 test4890 test4891 test4892 test4893 test4894 test4895 test4896 test4897 test4898 test4899 test4900 test4901 test4902 test4903 test4904 test4905 test4906 test4907 test4908 test4909 test4910 test4911 test4912 test4913 test4914 test4915 test4916 test4917 test4918 test4919 test4920 test4921 test4922 test4923 test4924 test4925 test4926 test4927 test4928 test4929 test4930 test4931 test4932 test4933 test4934 test4935 test4936 test4937 test4938 test4939 test4940 test4941 test4942 test4943 test4944 test4945 test4946 test4947 test4948 test4949 test4950 test4951 test4952 test4953 test4954 test4955 test4956 test4957 test4958 test4959 test4960 test4961 test4962 test4963 test4964 test4965 test4966 test4967 test4968 test4969 test4970 test4971 test4972 test4973 test4974 test4975 test4976 test4977 test4978 test4979 test4980 test4981 test4982 test4983 test4984 test4985 test4986 test4987 test4988 test4989 test4990 test4991 test4992 test4993 test4994 test4995 test4996 test4997 test4998 test4999 test5000 test5001 test5002 test5003 test5004 test5005 test5006 test5007 test5008 test5009 test5010 test5011 test5012 test5013 test5014 test5015 test5016 test5017 test5018 test5019 test5020 test5021 test5022 test5023 test5024 test5025 test5026 test5027 test5028 test5029 test5030 test5031 test5032 test5033 test5034 test5035 test5036 test5037 test5038 test5039 test5040 test5041 test5042 test5043 test5044 test5045 test5046 test5047 test5048 test5049 test5050 test5051 test5052 test5053 test5054 test5055 test5056 test5057 test5058 test5059 test5060 test5061 test5062 test5063 test5064 test5065 test5066 test5067 test5068 test5069 test5070 test5071 test5072 test5073 test5074 test5075 test5076 test5077 test5078 test5079 test5080 test5081 test5082 test5083 test5084 test5085 test5086 test5087 test5088 test5089 test5090 test5091 test5092 test5093 test5094 test5095 test5096 test5097 test5098 test5099 test5100 test5101 test5102 test5103 test5104 test5105 test5106 test5107 test5108 test5109 test5110 test5111 test5112 test5113 test5114 test5115 test5116 test5117 test5118 test5119 test5120 test5121 test5122 test5123 test5124 test5125 test5126 test5127 test5128 test5129 test5130 test5131 test5132 test5133 test5134 test5135 test5136 test5137 test5138 test5139 test5140 test5141 test5142 test5143 test5144 test5145 test5146 test5147 test5148 test5149 test5150 test5151 test5152 test5153 test5154 test5155 test5156 test5157 test5158 test5159 test5160 test5161 test5162 test5163 test5164 test5165 test5166 test5167 test5168 test5169 test5170 test5171 test5172 test5173 test5174 test5175 test5176 test5177 test5178 test5179 test5180 test5181 test5182 test5183 test5184 test5185 test5186 test5187 test5188 test5189 test5190 test5191 test5192 test5193 test5194 test5195 test5196 test5197 test5198 test5199 test5200 test5201 test5202 test5203 test5204 test5205 test5206 test5207 test5208 test5209 test5210 test5211 test5212 test5213 test5214 test5215 test5216 test5217 test5218 test5219 test5220 test5221 test5222 test5223 test5224 test5225 test5226 test5227 test5228 test5229 test5230 test5231 test5232 test5233 test5234 test5235 test5236 test5237 test5238 test5239 test5240 test5241 test5242 test5243 test5244 test5245 test5246 test5247 test5248 test5249 test5250 test5251 test5252 test5253 test5254 test5255 test5256 test5257 test5258 test5259 test5260 test5261 test5262 test5263 test5264 test5265 test5266 test5267 test5268 test5269 test5270 test5271 test5272 test5273 test5274 test5275 test5276 test5277 test5278 test5279 test5280 test5281 test5282 test5283 test5284 test5285 test5286 test5287 test5288 test5289 test5290 test5291 test5292 test5293 test5294 test5295 test5296 test5297 test5298 test5299 test5300 test5301 test5302 test5303 test5304 test5305 test5306 test5307 test5308 test5309 test5310 test5311 test5312 test5313 test5314 test5315 test5316 test5317 test5318 test5319 test5320 test5321 test5322 test5323 test5324 test5325 test5326 test5327 test5328 test5329 test5330 test5331 test5332 test5333 test5334 test5335 test5336 test5337 test5338 test5339 test5340 test5341 test5342 test5343 test5344 test5345 test5346 test5347 test5348 test5349 test5350 test5351 test5352 test5353 test5354 test5355 test5356 test5357 test5358 test5359 test5360 test5361 test5362 test5363 test5364 test5365 test5366 test5367 test5368 test5369 test5370 test5371 test5372 test5373 test5374 test5375 test5376 test5377 test5378 test5379 test5380 test5381 test5382 test5383 test5384 test5385 test5386 test5387 test5388 test5389 test5390 test5391 test5392 test5393 test5394 test5395 test5396 test5397 test5398 test5399 test5400 test5401 test5402 test5403 test5404 test5405 test5406 test5407 test5408 test5409 test5410 test5411 test5412 test5413 test5414 test5415 test5416 test5417 test5418 test5419 test5420 test5421 test5422 test5423 test5424 test5425 test5426 test5427 test5428 test5429 test5430 test5431 test5432 test5433 test5434 test5435 test5436 test5437 test5438 test5439 test5440 test5441 test5442 test5443 test5444 test5445 test5446 test5447 test5448 test5449 test5450 test5451 test5452 test5453 test5454 test5455 test5456 test5457 test5458 test5459 test5460 test5461 test5462 test5463 test5464 test5465 test5466 test5467 test5468 test5469 test5470 test5471 test5472 test5473 test5474 test5475 test5476 test5477 test5478 test5479 test5480 test5481 test5482 test5483 test5484 test5485 test5486 test5487 test5488 test5489 test5490 test5491 test5492 test5493 test5494 test5495 test5496 test5497 test5498 test5499 test5500 test5501 test5502 test5503 test5504 test5505 test5506 test5507 test5508 test5509 test5510 test5511 test5512 test5513 test5514 test5515 test5516 test5517 test5518 test5519 test5520 test5521 test5522 test5523 test5524 test5525 test5526 test5527 test5528 test5529 test5530 test5531 test5532 test5533 test5534 test5535 test5536 test5537 test5538 test5539 test5540 test5541 test5542 test5543 test5544 test5545 test5546 test5547 test5548 test5549 test5550 test5551 test5552 test5553 test5554 test5555 test5556 test5557 test5558 test5559 test5560 test5561 test5562 test5563 test5564 test5565 test5566 test5567 test5568 test5569 test5570 test5571 test5572 test5573 test5574 test5575 test5576 test5577 test5578 test5579 test5580 test5581 test5582 test5583 test5584 test5585 test5586 test5587 test5588 test5589 test5590 test5591 test5592 test5593 test5594 test5595 test5596 test5597 test5598 test5599 test5600 test5601 test5602 test5603 test5604 test5605 test5606 test5607 test5608 test5609 test5610 test5611 test5612 test5613 test5614 test5615 test5616 test5617 test5618 test5619 test5620 test5621 test5622 test5623 test5624 test5625 test5626 test5627 test5628 test5629 test5630 test5631 test5632 test5633 test5634 test5635 test5636 test5637 test5638 test5639 test5640 test5641 test5642 test5643 test5644 test5645 test5646 test5647 test5648 test5649 test5650 test5651 test5652 test5653 test5654 test5655 test5656 test5657 test5658 test5659 test5660 test5661 test5662 test5663 test5664 test5665 test5666 test5667 test5668 test5669 test5670 test5671 test5672 test5673 test5674 test5675 test5676 test5677 test5678 test5679 test5680 test5681 test5682 test5683 test5684 test5685 test5686 test5687 test5688 test5689 test5690 test5691 test5692 test5693 test5694 test5695 test5696 test5697 test5698 test5699 test5700 test5701 test5702 test5703 test5704 test5705 test5706 test5707 test5708 test5709 test5710 test5711 test5712 test5713 test5714 test5715 test5716 test5717 test5718 test5719 test5720 test5721 test5722 test5723 test5724 test5725 test5726 test5727 test5728 test5729 test5730 test5731 test5732 test5733 test5734 test5735 test5736 test5737 test5738 test5739 test5740 test5741 test5742 test5743 test5744 test5745 test5746 test5747 test5748 test5749 test5750 test5751 test5752 test5753 test5754 test5755 test5756 test5757 test5758 test5759 test5760 test5761 test5762 test5763 test5764 test5765 test5766 test5767 test5768 test5769 test5770 test5771 test5772 test5773 test5774 test5775 test5776 test5777 test5778 test5779 test5780 test5781 test5782 test5783 test5784 test5785 test5786 test5787 test5788 test5789 test5790 test5791 test5792 test5793 test5794 test5795 test5796 test5797 test5798 test5799 test5800 test5801 test5802 test5803 test5804 test5805 test5806 test5807 test5808 test5809 test5810 test5811 test5812 test5813 test5814 test5815 test5816 test5817 test5818 test5819 test5820 test5821 test5822 test5823 test5824 test5825 test5826 test5827 test5828 test5829 test5830 test5831 test5832 test5833 test5834 test5835 test5836 test5837 test5838 test5839 test5840 test5841 test5842 test5843 test5844 test5845 test5846 test5847 test5848 test5849 test5850 test5851 test5852 test5853 test5854 test5855 test5856 test5857 test5858 test5859 test5860 test5861 test5862 test5863 test5864 test5865 test5866 test5867 test5868 test5869 test5870 test5871 test5872 test5873 test5874 test5875 test5876 test5877 test5878 test5879 test5880 test5881 test5882 test5883 test5884 test5885 test5886 test5887 test5888 test5889 test5890 test5891 test5892 test5893 test5894 test5895 test5896 test5897 test5898 test5899 test5900 test5901 test5902 test5903 test5904 test5905 test5906 test5907 test5908 test5909 test5910 test5911 test5912 test5913 test5914 test5915 test5916 test5917 test5918 test5919 test5920 test5921 test5922 test5923 test5924 test5925 test5926 test5927 test5928 test5929 test5930 test5931 test5932 test5933 test5934 test5935 test5936 test5937 test5938 test5939 test5940 test5941 test5942 test5943 test5944 test5945 test5946 test5947 test5948 test5949 test5950 test5951 test5952 test5953 test5954 test5955 test5956 test5957 test5958 test5959 test5960 test5961 test5962 test5963 test5964 test5965 test5966 test5967 test5968 test5969 test5970 test5971 test5972 test5973 test5974 test5975 test5976 test5977 test5978 test5979 test5980 test5981 test5982 test5983 test5984 test5985 test5986 test5987 test5988 test5989 test5990 test5991 test5992 test5993 test5994 test5995 test5996 test5997 test5998 test5999 test6000 test6001 test6002 test6003 test6004 test6005 test6006 test6007 test6008 test6009 test6010 test6011 test6012 test6013 test6014 test6015 test6016 test6017 test6018 test6019 test6020 test6021 test6022 test6023 test6024 test6025 test6026 test6027 test6028 test6029 test6030 test6031 test6032 test6033 test6034 test6035 test6036 test6037 test6038 test6039 test6040 test6041 test6042 test6043 test6044 test6045 test6046 test6047 test6048 test6049 test6050 test6051 test6052 test6053 test6054 test6055 test6056 test6057 test6058 test6059 test6060 test6061 test6062 test6063 test6064 test6065 test6066 test6067 test6068 test6069 test6070 test6071 test6072 test6073 test6074 test6075 test6076 test6077 test6078 test6079 test6080 test6081 test6082 test6083 test6084 test6085 test6086 test6087 test6088 test6089 test6090 test6091 test6092 test6093 test6094 test6095 test6096 test6097 test6098 test6099 test6100 test6101 test6102 test6103 test6104 test6105 test6106 test6107 test6108 test6109 test6110 test6111 test6112 test6113 test6114 test6115 test6116 test6117 test6118 test6119 test6120 test6121 test6122 test6123 test6124 test6125 test6126 test6127 test6128 test6129 test6130 test6131 test6132 test6133 test6134 test6135 test6136 test6137 test6138 test6139 test6140 test6141 test6142 test6143 test6144 test6145 test6146 test6147 test6148 test6149 test6150 test6151 test6152 test6153 test6154 test6155 test6156 test6157 test6158 test6159 test6160 test6161 test6162 test6163 test6164 test6165 test6166 test6167 test6168 test6169 test6170 test6171 test6172 test6173 test6174 test6175 test6176 test6177 test6178 test6179 test6180 test6181 test6182 test6183 test6184 test6185 test6186 test6187 test6188 test6189 test6190 test6191 test6192 test6193 test6194 test6195 test6196 test6197 test6198 test6199 test6200 test6201 test6202 test6203 test6204 test6205 test6206 test6207 test6208 test6209 test6210 test6211 test6212 test6213 test6214 test6215 test6216 test6217 test6218 test6219 test6220 test6221 test6222 test6223 test6224 test6225 test6226 test6227 test6228 test6229 test6230 test6231 test6232 test6233 test6234 test6235 test6236 test6237 test6238 test6239 test6240 test6241 test6242 test6243 test6244 test6245 test6246 test6247 test6248 test6249 test6250 test6251 test6252 test6253 test6254 test6255 test6256 test6257 test6258 test6259 test6260 test6261 test6262 test6263 test6264 test6265 test6266 test6267 test6268 test6269 test6270 test6271 test6272 test6273 test6274 test6275 test6276 test6277 test6278 test6279 test6280 test6281 test6282 test6283 test6284 test6285 test6286 test6287 test6288 test6289 test6290 test6291 test6292 test6293 test6294 test6295 test6296 test6297 test6298 test6299 test6300 test6301 test6302 test6303 test6304 test6305 test6306 test6307 test6308 test6309 test6310 test6311 test6312 test6313 test6314 test6315 test6316 test6317 test6318 test6319 test6320 test6321 test6322 test6323 test6324 test6325 test6326 test6327 test6328 test6329 test6330 test6331 test6332 test6333 test6334 test6335 test6336 test6337 test6338 test6339 test6340 test6341 test6342 test6343 test6344 test6345 test6346 test6347 test6348 test6349 test6350 test6351 test6352 test6353 test6354 test6355 test6356 test6357 test6358 test6359 test6360 test6361 test6362 test6363 test6364 test6365 test6366 test6367 test6368 test6369 test6370 test6371 test6372 test6373 test6374 test6375 test6376 test6377 test6378 test6379 test6380 test6381 test6382 test6383 test6384 test6385 test6386 test6387 test6388 test6389 test6390 test6391 test6392 test6393 test6394 test6395 test6396 test6397 test6398 test6399 test6400 test6401 test6402 test6403 test6404 test6405 test6406 test6407 test6408 test6409 test6410 test6411 test6412 test6413 test6414 test6415 test6416 test6417 test6418 test6419 test6420 test6421 test6422 test6423 test6424 test6425 test6426 test6427 test6428 test6429 test6430 test6431 test6432 test6433 test6434 test6435 test6436 test6437 test6438 test6439 test6440 test6441 test6442 test6443 test6444 test6445 test6446 test6447 test6448 test6449 test6450 test6451 test6452 test6453 test6454 test6455 test6456 test6457 test6458 test6459 test6460 test6461 test6462 test6463 test6464 test6465 test6466 test6467 test6468 test6469 test6470 test6471 test6472 test6473 test6474 test6475 test6476 test6477 test6478 test6479 test6480 test6481 test6482 test6483 test6484 test6485 test6486 test6487 test6488 test6489 test6490 test6491 test6492 test6493 test6494 test6495 test6496 test6497 test6498 test6499 test6500 test6501 test6502 test6503 test6504 test6505 test6506 test6507 test6508 test6509 test6510 test6511 test6512 test6513 test6514 test6515 test6516 test6517 test6518 test6519 test6520 test6521 test6522 test6523 test6524 test6525 test6526 test6527 test6528 test6529 test6530 test6531 test6532 test6533 test6534 test6535 test6536 test6537 test6538 test6539 test6540 test6541 test6542 test6543 test6544 test6545 test6546 test6547 test6548 test6549 test6550 test6551 test6552 test6553 test6554 test6555 test6556 test6557 test6558 test6559 test6560 test6561 test6562 test6563 test6564 test6565 test6566 test6567 test6568 test6569 test6570 test6571 test6572 test6573 test6574 test6575 test6576 test6577 test6578 test6579 test6580 test6581 test6582 test6583 test6584 test6585 test6586 test6587 test6588 test6589 test6590 test6591 test6592 test6593 test6594 test6595 test6596 test6597 test6598 test6599 test6600 test6601 test6602 test6603 test6604 test6605 test6606 test6607 test6608 test6609 test6610 test6611 test6612 test6613 test6614 test6615 test6616 test6617 test6618 test6619 test6620 test6621 test6622 test6623 test6624 test6625 test6626 test6627 test6628 test6629 test6630 test6631 test6632 test6633 test6634 test6635 test6636 test6637 test6638 test6639 test6640 test6641 test6642 test6643 test6644 test6645 test6646 test6647 test6648 test6649 test6650 test6651 test6652 test6653 test6654 test6655 test6656 test6657 test6658 test6659 test6660 test6661 test6662 test6663 test6664 test6665 test6666 test6667 test6668 test6669 test6670 test6671 test6672 test6673 test6674 test6675 test6676 test6677 test6678 test6679 test6680 test6681 test6682 test6683 test6684 test6685 test6686 test6687 test6688 test6689 test6690 test6691 test6692 test6693 test6694 test6695 test6696 test6697 test6698 test6699 test6700 test6701 test6702 test6703 test6704 test6705 test6706 test6707 test6708 test6709 test6710 test6711 test6712 test6713 test6714 test6715 test6716 test6717 test6718 test6719 test6720 test6721 test6722 test6723 test6724 test6725 test6726 test6727 test6728 test6729 test6730 test6731 test6732 test6733 test6734 test6735 test6736 test6737 test6738 test6739 test6740 test6741 test6742 test6743 test6744 test6745 test6746 test6747 test6748 test6749 test6750 test6751 test6752 test6753 test6754 test6755 test6756 test6757 test6758 test6759 test6760 test6761 test6762 test6763 test6764 test6765 test6766 test6767 test6768 test6769 test6770 test6771 test6772 test6773 test6774 test6775 test6776 test6777 test6778 test6779 test6780 test6781 test6782 test6783 test6784 test6785 test6786 test6787 test6788 test6789 test6790 test6791 test6792 test6793 test6794 test6795 test6796 test6797 test6798 test6799 test6800 test6801 test6802 test6803 test6804 test6805 test6806 test6807 test6808 test6809 test6810 test6811 test6812 test6813 test6814 test6815 test6816 test6817 test6818 test6819 test6820 test6821 test6822 test6823 test6824 test6825 test6826 test6827 test6828 test6829 test6830 test6831 test6832 test6833 test6834 test6835 test6836 test6837 test6838 test6839 test6840 test6841 test6842 test6843 test6844 test6845 test6846 test6847 test6848 test6849 test6850 test6851 test6852 test6853 test6854 test6855 test6856 test6857 test6858 test6859 test6860 test6861 test6862 test6863 test6864 test6865 test6866 test6867 test6868 test6869 test6870 test6871 test6872 test6873 test6874 test6875 test6876 test6877 test6878 test6879 test6880 test6881 test6882 test6883 test6884 test6885 test6886 test6887 test6888 test6889 test6890 test6891 test6892 test6893 test6894 test6895 test6896 test6897 test6898 test6899 test6900 test6901 test6902 test6903 test6904 test6905 test6906 test6907 test6908 test6909 test6910 test6911 test6912 test6913 test6914 test6915 test6916 test6917 test6918 test6919 test6920 test6921 test6922 test6923 test6924 test6925 test6926 test6927 test6928 test6929 test6930 test6931 test6932 test6933 test6934 test6935 test6936 test6937 test6938 test6939 test6940 test6941 test6942 test6943 test6944 test6945 test6946 test6947 test6948 test6949 test6950 test6951 test6952 test6953 test6954 test6955 test6956 test6957 test6958 test6959 test6960 test6961 test6962 test6963 test6964 test6965 test6966 test6967 test6968 test6969 test6970 test6971 test6972 test6973 test6974 test6975 test6976 test6977 test6978 test6979 test6980 test6981 test6982 test6983 test6984 test6985 test6986 test6987 test6988 test6989 test6990 test6991 test6992 test6993 test6994 test6995 test6996 test6997 test6998 test6999 test7000 test7001 test7002 test7003 test7004 test7005 test7006 test7007 test7008 test7009 test7010 test7011 test7012 test7013 test7014 test7015 test7016 test7017 test7018 test7019 test7020 test7021 test7022 test7023 test7024 test7025 test7026 test7027 test7028 test7029 test7030 test7031 test7032 test7033 test7034 test7035 test7036 test7037 test7038 test7039 test7040 test7041 test7042 test7043 test7044 test7045 test7046 test7047 test7048 test7049 test7050 test7051 test7052 test7053 test7054 test7055 test7056 test7057 test7058 test7059 test7060 test7061 test7062 test7063 test7064 test7065 test7066 test7067 test7068 test7069 test7070 test7071 test7072 test7073 test7074 test7075 test7076 test7077 test7078 test7079 test7080 test7081 test7082 test7083 test7084 test7085 test7086 test7087 test7088 test7089 test7090 test7091 test7092 test7093 test7094 test7095 test7096 test7097 test7098 test7099 test7100 test7101 test7102 test7103 test7104 test7105 test7106 test7107 test7108 test7109 test7110 test7111 test7112 test7113 test7114 test7115 test7116 test7117 test7118 test7119 test7120 test7121 test7122 test7123 test7124 test7125 test7126 test7127 test7128 test7129 test7130 test7131 test7132 test7133 test7134 test7135 test7136 test7137 test7138 test7139 test7140 test7141 test7142 test7143 test7144 test7145 test7146 test7147 test7148 test7149 test7150 test7151 test7152 test7153 test7154 test7155 test7156 test7157 test7158 test7159 test7160 test7161 test7162 test7163 test7164 test7165 test7166 test7167 test7168 test7169 test7170 test7171 test7172 test7173 test7174 test7175 test7176 test7177 test7178 test7179 test7180 test7181 test7182 test7183 test7184 test7185 test7186 test7187 test7188 test7189 test7190 test7191 test7192 test7193 test7194 test7195 test7196 test7197 test7198 test7199 test7200 test7201 test7202 test7203 test7204 test7205 test7206 test7207 test7208 test7209 test7210 test7211 test7212 test7213 test7214 test7215 test7216 test7217 test7218 test7219 test7220 test7221 test7222 test7223 test7224 test7225 test7226 test7227 test7228 test7229 test7230 test7231 test7232 test7233 test7234 test7235 test7236 test7237 test7238 test7239 test7240 test7241 test7242 test7243 test7244 test7245 test7246 test7247 test7248 test7249 test7250 test7251 test7252 test7253 test7254 test7255 test7256 test7257 test7258 test7259 test7260 test7261 test7262 test7263 test7264 test7265 test7266 test7267 test7268 test7269 test7270 test7271 test7272 test7273 test7274 test7275 test7276 test7277 test7278 test7279 test7280 test7281 test7282 test7283 test7284 test7285 test7286 test7287 test7288 test7289 test7290 test7291 test7292 test7293 test7294 test7295 test7296 test7297 test7298 test7299 test7300 test7301 test7302 test7303 test7304 test7305 test7306 test7307 test7308 test7309 test7310 test7311 test7312 test7313 test7314 test7315 test7316 test7317 test7318 test7319 test7320 test7321 test7322 test7323 test7324 test7325 test7326 test7327 test7328 test7329 test7330 test7331 test7332 test7333 test7334 test7335 test7336 test7337 test7338 test7339 test7340 test7341 test7342 test7343 test7344 test7345 test7346 test7347 test7348 test7349 test7350 test7351 test7352 test7353 test7354 test7355 test7356 test7357 test7358 test7359 test7360 test7361 test7362 test7363 test7364 test7365 test7366 test7367 test7368 test7369 test7370 test7371 test7372 test7373 test7374 test7375 test7376 test7377 test7378 test7379 test7380 test7381 test7382 test7383 test7384 test7385 test7386 test7387 test7388 test7389 test7390 test7391 test7392 test7393 test7394 test7395 test7396 test7397 test7398 test7399 test7400 test7401 test7402 test7403 test7404 test7405 test7406 test7407 test7408 test7409 test7410 test7411 test7412 test7413 test7414 test7415 test7416 test7417 test7418 test7419 test7420 test7421 test7422 test7423 test7424 test7425 test7426 test7427 test7428 test7429 test7430 test7431 test7432 test7433 test7434 test7435 test7436 test7437 test7438 test7439 test7440 test7441 test7442 test7443 test7444 test7445 test7446 test7447 test7448 test7449 test7450 test7451 test7452 test7453 test7454 test7455 test7456 test7457 test7458 test7459 test7460 test7461 test7462 test7463 test7464 test7465 test7466 test7467 test7468 test7469 test7470 test7471 test7472 test7473 test7474 test7475 test7476 test7477 test7478 test7479 test7480 test7481 test7482 test7483 test7484 test7485 test7486 test7487 test7488 test7489 test7490 test7491 test7492 test7493 test7494 test7495 test7496 test7497 test7498 test7499 test7500 test7501 test7502 test7503 test7504 test7505 test7506 test7507 test7508 test7509 test7510 test7511 test7512 test7513 test7514 test7515 test7516 test7517 test7518 test7519 test7520 test7521 test7522 test7523 test7524 test7525 test7526 test7527 test7528 test7529 test7530 test7531 test7532 test7533 test7534 test7535 test7536 test7537 test7538 test7539 test7540 test7541 test7542 test7543 test7544 test7545 test7546 test7547 test7548 test7549 test7550 test7551 test7552 test7553 test7554 test7555 test7556 test7557 test7558 test7559 test7560 test7561 test7562 test7563 test7564 test7565 test7566 test7567 test7568 test7569 test7570 test7571 test7572 test7573 test7574 test7575 test7576 test7577 test7578 test7579 test7580 test7581 test7582 test7583 test7584 test7585 test7586 test7587 test7588 test7589 test7590 test7591 test7592 test7593 test7594 test7595 test7596 test7597 test7598 test7599 test7600 test7601 test7602 test7603 test7604 test7605 test7606 test7607 test7608 test7609 test7610 test7611 test7612 test7613 test7614 test7615 test7616 test7617 test7618 test7619 test7620 test7621 test7622 test7623 test7624 test7625 test7626 test7627 test7628 test7629 test7630 test7631 test7632 test7633 test7634 test7635 test7636 test7637 test7638 test7639 test7640 test7641 test7642 test7643 test7644 test7645 test7646 test7647 test7648 test7649 test7650 test7651 test7652 test7653 test7654 test7655 test7656 test7657 test7658 test7659 test7660 test7661 test7662 test7663 test7664 test7665 test7666 test7667 test7668 test7669 test7670 test7671 test7672 test7673 test7674 test7675 test7676 test7677 test7678 test7679 test7680 test7681 test7682 test7683 test7684 test7685 test7686 test7687 test7688 test7689 test7690 test7691 test7692 test7693 test7694 test7695 test7696 test7697 test7698 test7699 test7700 test7701 test7702 test7703 test7704 test7705 test7706 test7707 test7708 test7709 test7710 test7711 test7712 test7713 test7714 test7715 test7716 test7717 test7718 test7719 test7720 test7721 test7722 test7723 test7724 test7725 test7726 test7727 test7728 test7729 test7730 test7731 test7732 test7733 test7734 test7735 test7736 test7737 test7738 test7739 test7740 test7741 test7742 test7743 test7744 test7745 test7746 test7747 test7748 test7749 test7750 test7751 test7752 test7753 test7754 test7755 test7756 test7757 test7758 test7759 test7760 test7761 test7762 test7763 test7764 test7765 test7766 test7767 test7768 test7769 test7770 test7771 test7772 test7773 test7774 test7775 test7776 test7777 test7778 test7779 test7780 test7781 test7782 test7783 test7784 test7785 test7786 test7787 test7788 test7789 test7790 test7791 test7792 test7793 test7794 test7795 test7796 test7797 test7798 test7799 test7800 test7801 test7802 test7803 test7804 test7805 test7806 test7807 test7808 test7809 test7810 test7811 test7812 test7813 test7814 test7815 test7816 test7817 test7818 test7819 test7820 test7821 test7822 test7823 test7824 test7825 test7826 test7827 test7828 test7829 test7830 test7831 test7832 test7833 test7834 test7835 test7836 test7837 test7838 test7839 test7840 test7841 test7842 test7843 test7844 test7845 test7846 test7847 test7848 test7849 test7850 test7851 test7852 test7853 test7854 test7855 test7856 test7857 test7858 test7859 test7860 test7861 test7862 test7863 test7864 test7865 test7866 test7867 test7868 test7869 test7870 test7871 test7872 test7873 test7874 test7875 test7876 test7877 test7878 test7879 test7880 test7881 test7882 test7883 test7884 test7885 test7886 test7887 test7888 test7889 test7890 test7891 test7892 test7893 test7894 test7895 test7896 test7897 test7898 test7899 test7900 test7901 test7902 test7903 test7904 test7905 test7906 test7907 test7908 test7909 test7910 test7911 test7912 test7913 test7914 test7915 test7916 test7917 test7918 test7919 test7920 test7921 test7922 test7923 test7924 test7925 test7926 test7927 test7928 test7929 test7930 test7931 test7932 test7933 test7934 test7935 test7936 test7937 test7938 test7939 test7940 test7941 test7942 test7943 test7944 test7945 test7946 test7947 test7948 test7949 test7950 test7951 test7952 test7953 test7954 test7955 test7956 test7957 test7958 test7959 test7960 test7961 test7962 test7963 test7964 test7965 test7966 test7967 test7968 test7969 test7970 test7971 test7972 test7973 test7974 test7975 test7976 test7977 test7978 test7979 test7980 test7981 test7982 test7983 test7984 test7985 test7986 test7987 test7988 test7989 test7990 test7991 test7992 test7993 test7994 test7995 test7996 test7997 test7998 test7999 test8000 test8001 test8002 test8003 test8004 test8005 test8006 test8007 test8008 test8009 test8010 test8011 test8012 test8013 test8014 test8015 test8016 test8017 test8018 test8019 test8020 test8021 test8022 test8023 test8024 test8025 test8026 test8027 test8028 test8029 test8030 test8031 test8032 test8033 test8034 test8035 test8036 test8037 test8038 test8039 test8040 test8041 test8042 test8043 test8044 test8045 test8046 test8047 test8048 test8049 test8050 test8051 test8052 test8053 test8054 test8055 test8056 test8057 test8058 test8059 test8060 test8061 test8062 test8063 test8064 test8065 test8066 test8067 test8068 test8069 test8070 test8071 test8072 test8073 test8074 test8075 test8076 test8077 test8078 test8079 test8080 test8081 test8082 test8083 test8084 test8085 test8086 test8087 test8088 test8089 test8090 test8091 test8092 test8093 test8094 test8095 test8096 test8097 test8098 test8099 test8100 test8101 test8102 test8103 test8104 test8105 test8106 test8107 test8108 test8109 test8110 test8111 test8112 test8113 test8114 test8115 test8116 test8117 test8118 test8119 test8120 test8121 test8122 test8123 test8124 test8125 test8126 test8127 test8128 test8129 test8130 test8131 test8132 test8133 test8134 test8135 test8136 test8137 test8138 test8139 test8140 test8141 test8142 test8143 test8144 test8145 test8146 test8147 test8148 test8149 test8150 test8151 test8152 test8153 test8154 test8155 test8156 test8157 test8158 test8159 test8160 test8161 test8162 test8163 test8164 test8165 test8166 test8167 test8168 test8169 test8170 test8171 test8172 test8173 test8174 test8175 test8176 test8177 test8178 test8179 test8180 test8181 test8182 test8183 test8184 test8185 test8186 test8187 test8188 test8189 test8190 test8191 test8192 test8193 test8194 test8195 test8196 test8197 test8198 test8199 test8200 test8201 test8202 test8203 test8204 test8205 test8206 test8207 test8208 test8209 test8210 test8211 test8212 test8213 test8214 test8215 test8216 test8217 test8218 test8219 test8220 test8221 test8222 test8223 test8224 test8225 test8226 test8227 test8228 test8229 test8230 test8231 test8232 test8233 test8234 test8235 test8236 test8237 test8238 test8239 test8240 test8241 test8242 test8243 test8244 test8245 test8246 test8247 test8248 test8249 test8250 test8251 test8252 test8253 test8254 test8255 test8256 test8257 test8258 test8259 test8260 test8261 test8262 test8263 test8264 test8265 test8266 test8267 test8268 test8269 test8270 test8271 test8272 test8273 test8274 test8275 test8276 test8277 test8278 test8279 test8280 test8281 test8282 test8283 test8284 test8285 test8286 test8287 test8288 test8289 test8290 test8291 test8292 test8293 test8294 test8295 test8296 test8297 test8298 test8299 test8300 test8301 test8302 test8303 test8304 test8305 test8306 test8307 test8308 test8309 test8310 test8311 test8312 test8313 test8314 test8315 test8316 test8317 test8318 test8319 test8320 test8321 test8322 test8323 test8324 test8325 test8326 test8327 test8328 test8329 test8330 test8331 test8332 test8333 test8334 test8335 test8336 test8337 test8338 test8339 test8340 test8341 test8342 test8343 test8344 test8345 test8346 test8347 test8348 test8349 test8350 test8351 test8352 test8353 test8354 test8355 test8356 test8357 test8358 test8359 test8360 test8361 test8362 test8363 test8364 test8365 test8366 test8367 test8368 test8369 test8370 test8371 test8372 test8373 test8374 test8375 test8376 test8377 test8378 test8379 test8380 test8381 test8382 test8383 test8384 test8385 test8386 test8387 test8388 test8389 test8390 test8391 test8392 test8393 test8394 test8395 test8396 test8397 test8398 test8399 test8400 test8401 test8402 test8403 test8404 test8405 test8406 test8407 test8408 test8409 test8410 test8411 test8412 test8413 test8414 test8415 test8416 test8417 test8418 test8419 test8420 test8421 test8422 test8423 test8424 test8425 test8426 test8427 test8428 test8429 test8430 test8431 test8432 test8433 test8434 test8435 test8436 test8437 test8438 test8439 test8440 test8441 test8442 test8443 test8444 test8445 test8446 test8447 test8448 test8449 test8450 test8451 test8452 test8453 test8454 test8455 test8456 test8457 test8458 test8459 test8460 test8461 test8462 test8463 test8464 test8465 test8466 test8467 test8468 test8469 test8470 test8471 test8472 test8473 test8474 test8475 test8476 test8477 test8478 test8479 test8480 test8481 test8482 test8483 test8484 test8485 test8486 test8487 test8488 test8489 test8490 test8491 test8492 test8493 test8494 test8495 test8496 test8497 test8498 test8499 test8500 test8501 test8502 test8503 test8504 test8505 test8506 test8507 test8508 test8509 test8510 test8511 test8512 test8513 test8514 test8515 test8516 test8517 test8518 test8519 test8520 test8521 test8522 test8523 test8524 test8525 test8526 test8527 test8528 test8529 test8530 test8531 test8532 test8533 test8534 test8535 test8536 test8537 test8538 test8539 test8540 test8541 test8542 test8543 test8544 test8545 test8546 test8547 test8548 test8549 test8550 test8551 test8552 test8553 test8554 test8555 test8556 test8557 test8558 test8559 test8560 test8561 test8562 test8563 test8564 test8565 test8566 test8567 test8568 test8569 test8570 test8571 test8572 test8573 test8574 test8575 test8576 test8577 test8578 test8579 test8580 test8581 test8582 test8583 test8584 test8585 test8586 test8587 test8588 test8589 test8590 test8591 test8592 test8593 test8594 test8595 test8596 test8597 test8598 test8599 test8600 test8601 test8602 test8603 test8604 test8605 test8606 test8607 test8608 test8609 test8610 test8611 test8612 test8613 test8614 test8615 test8616 test8617 test8618 test8619 test8620 test8621 test8622 test8623 test8624 test8625 test8626 test8627 test8628 test8629 test8630 test8631 test8632 test8633 test8634 test8635 test8636 test8637 test8638 test8639 test8640 test8641 test8642 test8643 test8644 test8645 test8646 test8647 test8648 test8649 test8650 test8651 test8652 test8653 test8654 test8655 test8656 test8657 test8658 test8659 test8660 test8661 test8662 test8663 test8664 test8665 test8666 test8667 test8668 test8669 test8670 test8671 test8672 test8673 test8674 test8675 test8676 test8677 test8678 test8679 test8680 test8681 test8682 test8683 test8684 test8685 test8686 test8687 test8688 test8689 test8690 test8691 test8692 test8693 test8694 test8695 test8696 test8697 test8698 test8699 test8700 test8701 test8702 test8703 test8704 test8705 test8706 test8707 test8708 test8709 test8710 test8711 test8712 test8713 test8714 test8715 test8716 test8717 test8718 test8719 test8720 test8721 test8722 test8723 test8724 test8725 test8726 test8727 test8728 test8729 test8730 test8731 test8732 test8733 test8734 test8735 test8736 test8737 test8738 test8739 test8740 test8741 test8742 test8743 test8744 test8745 test8746 test8747 test8748 test8749 test8750 test8751 test8752 test8753 test8754 test8755 test8756 test8757 test8758 test8759 test8760 test8761 test8762 test8763 test8764 test8765 test8766 test8767 test8768 test8769 test8770 test8771 test8772 test8773 test8774 test8775 test8776 test8777 test8778 test8779 test8780 test8781 test8782 test8783 test8784 test8785 test8786 test8787 test8788 test8789 test8790 test8791 test8792 test8793 test8794 test8795 test8796 test8797 test8798 test8799 test8800 test8801 test8802 test8803 test8804 test8805 test8806 test8807 test8808 test8809 test8810 test8811 test8812 test8813 test8814 test8815 test8816 test8817 test8818 test8819 test8820 test8821 test8822 test8823 test8824 test8825 test8826 test8827 test8828 test8829 test8830 test8831 test8832 test8833 test8834 test8835 test8836 test8837 test8838 test8839 test8840 test8841 test8842 test8843 test8844 test8845 test8846 test8847 test8848 test8849 test8850 test8851 test8852 test8853 test8854 test8855 test8856 test8857 test8858 test8859 test8860 test8861 test8862 test8863 test8864 test8865 test8866 test8867 test8868 test8869 test8870 test8871 test8872 test8873 test8874 test8875 test8876 test8877 test8878 test8879 test8880 test8881 test8882 test8883 test8884 test8885 test8886 test8887 test8888 test8889 test8890 test8891 test8892 test8893 test8894 test8895 test8896 test8897 test8898 test8899 test8900 test8901 test8902 test8903 test8904 test8905 test8906 test8907 test8908 test8909 test8910 test8911 test8912 test8913 test8914 test8915 test8916 test8917 test8918 test8919 test8920 test8921 test8922 test8923 test8924 test8925 test8926 test8927 test8928 test8929 test8930 test8931 test8932 test8933 test8934 test8935 test8936 test8937 test8938 test8939 test8940 test8941 test8942 test8943 test8944 test8945 test8946 test8947 test8948 test8949 test8950 test8951 test8952 test8953 test8954 test8955 test8956 test8957 test8958 test8959 test8960 test8961 test8962 test8963 test8964 test8965 test8966 test8967 test8968 test8969 test8970 test8971 test8972 test8973 test8974 test8975 test8976 test8977 test8978 test8979 test8980 test8981 test8982 test8983 test8984 test8985 test8986 test8987 test8988 test8989 test8990 test8991 test8992 test8993 test8994 test8995 test8996 test8997 test8998 test8999 test9000 test9001 test9002 test9003 test9004 test9005 test9006 test9007 test9008 test9009 test9010 test9011 test9012 test9013 test9014 test9015 test9016 test9017 test9018 test9019 test9020 test9021 test9022 test9023 test9024 test9025 test9026 test9027 test9028 test9029 test9030 test9031 test9032 test9033 test9034 test9035 test9036 test9037 test9038 test9039 test9040 test9041 test9042 test9043 test9044 test9045 test9046 test9047 test9048 test9049 test9050 test9051 test9052 test9053 test9054 test9055 test9056 test9057 test9058 test9059 test9060 test9061 test9062 test9063 test9064 test9065 test9066 test9067 test9068 test9069 test9070 test9071 test9072 test9073 test9074 test9075 test9076 test9077 test9078 test9079 test9080 test9081 test9082 test9083 test9084 test9085 test9086 test9087 test9088 test9089 test9090 test9091 test9092 test9093 test9094 test9095 test9096 test9097 test9098 test9099 test9100 test9101 test9102 test9103 test9104 test9105 test9106 test9107 test9108 test9109 test9110 test9111 test9112 test9113 test9114 test9115 test9116 test9117 test9118 test9119 test9120 test9121 test9122 test9123 test9124 test9125 test9126 test9127 test9128 test9129 test9130 test9131 test9132 test9133 test9134 test9135 test9136 test9137 test9138 test9139 test9140 test9141 test9142 test9143 test9144 test9145 test9146 test9147 test9148 test9149 test9150 test9151 test9152 test9153 test9154 test9155 test9156 test9157 test9158 test9159 test9160 test9161 test9162 test9163 test9164 test9165 test9166 test9167 test9168 test9169 test9170 test9171 test9172 test9173 test9174 test9175 test9176 test9177 test9178 test9179 test9180 test9181 test9182 test9183 test9184 test9185 test9186 test9187 test9188 test9189 test9190 test9191 test9192 test9193 test9194 test9195 test9196 test9197 test9198 test9199 test9200 test9201 test9202 test9203 test9204 test9205 test9206 test9207 test9208 test9209 test9210 test9211 test9212 test9213 test9214 test9215 test9216 test9217 test9218 test9219 test9220 test9221 test9222 test9223 test9224 test9225 test9226 test9227 test9228 test9229 test9230 test9231 test9232 test9233 test9234 test9235 test9236 test9237 test9238 test9239 test9240 test9241 test9242 test9243 test9244 test9245 test9246 test9247 test9248 test9249 test9250 test9251 test9252 test9253 test9254 test9255 test9256 test9257 test9258 test9259 test9260 test9261 test9262 test9263 test9264 test9265 test9266 test9267 test9268 test9269 test9270 test9271 test9272 test9273 test9274 test9275 test9276 test9277 test9278 test9279 test9280 test9281 test9282 test9283 test9284 test9285 test9286 test9287 test9288 test9289 test9290 test9291 test9292 test9293 test9294 test9295 test9296 test9297 test9298 test9299 test9300 test9301 test9302 test9303 test9304 test9305 test9306 test9307 test9308 test9309 test9310 test9311 test9312 test9313 test9314 test9315 test9316 test9317 test9318 test9319 test9320 test9321 test9322 test9323 test9324 test9325 test9326 test9327 test9328 test9329 test9330 test9331 test9332 test9333 test9334 test9335 test9336 test9337 test9338 test9339 test9340 test9341 test9342 test9343 test9344 test9345 test9346 test9347 test9348 test9349 test9350 test9351 test9352 test9353 test9354 test9355 test9356 test9357 test9358 test9359 test9360 test9361 test9362 test9363 test9364 test9365 test9366 test9367 test9368 test9369 test9370 test9371 test9372 test9373 test9374 test9375 test9376 test9377 test9378 test9379 test9380 test9381 test9382 test9383 test9384 test9385 test9386 test9387 test9388 test9389 test9390 test9391 test9392 test9393 test9394 test9395 test9396 test9397 test9398 test9399 test9400 test9401 test9402 test9403 test9404 test9405 test9406 test9407 test9408 test9409 test9410 test9411 test9412 test9413 test9414 test9415 test9416 test9417 test9418 test9419 test9420 test9421 test9422 test9423 test9424 test9425 test9426 test9427 test9428 test9429 test9430 test9431 test9432 test9433 test9434 test9435 test9436 test9437 test9438 test9439 test9440 test9441 test9442 test9443 test9444 test9445 test9446 test9447 test9448 test9449 test9450 test9451 test9452 test9453 test9454 test9455 test9456 test9457 test9458 test9459 test9460 test9461 test9462 test9463 test9464 test9465 test9466 test9467 test9468 test9469 test9470 test9471 test9472 test9473 test9474 test9475 test9476 test9477 test9478 test9479 test9480 test9481 test9482 test9483 test9484 test9485 test9486 test9487 test9488 test9489 test9490 test9491 test9492 test9493 test9494 test9495 test9496 test9497 test9498 test9499 test9500 test9501 test9502 test9503 test9504 test9505 test9506 test9507 test9508 test9509 test9510 test9511 test9512 test9513 test9514 test9515 test9516 test9517 test9518 test9519 test9520 test9521 test9522 test9523 test9524 test9525 test9526 test9527 test9528 test9529 test9530 test9531 test9532 test9533 test9534 test9535 test9536 test9537 test9538 test9539 test9540 test9541 test9542 test9543 test9544 test9545 test9546 test9547 test9548 test9549 test9550 test9551 test9552 test9553 test9554 test9555 test9556 test9557 test9558 test9559 test9560 test9561 test9562 test9563 test9564 test9565 test9566 test9567 test9568 test9569 test9570 test9571 test9572 test9573 test9574 test9575 test9576 test9577 test9578 test9579 test9580 test9581 test9582 test9583 test9584 test9585 test9586 test9587 test9588 test9589 test9590 test9591 test9592 test9593 test9594 test9595 test9596 test9597 test9598 test9599 test9600 test9601 test9602 test9603 test9604 test9605 test9606 test9607 test9608 test9609 test9610 test9611 test9612 test9613 test9614 test9615 test9616 test9617 test9618 test9619 test9620 test9621 test9622 test9623 test9624 test9625 test9626 test9627 test9628 test9629 test9630 test9631 test9632 test9633 test9634 test9635 test9636 test9637 test9638 test9639 test9640 test9641 test9642 test9643 test9644 test9645 test9646 test9647 test9648 test9649 test9650 test9651 test9652 test9653 test9654 test9655 test9656 test9657 test9658 test9659 test9660 test9661 test9662 test9663 test9664 test9665 test9666 test9667 test9668 test9669 test9670 test9671 test9672 test9673 test9674 test9675 test9676 test9677 test9678 test9679 test9680 test9681 test9682 test9683 test9684 test9685 test9686 test9687 test9688 test9689 test9690 test9691 test9692 test9693 test9694 test9695 test9696 test9697 test9698 test9699 test9700 test9701 test9702 test9703 test9704 test9705 test9706 test9707 test9708 test9709 test9710 test9711 test9712 test9713 test9714 test9715 test9716 test9717 test9718 test9719 test9720 test9721 test9722 test9723 test9724 test9725 test9726 test9727 test9728 test9729 test9730 test9731 test9732 test9733 test9734 test9735 test9736 test9737 test9738 test9739 test9740 test9741 test9742 test9743 test9744 test9745 test9746 test9747 test9748 test9749 test9750 test9751 test9752 test9753 test9754 test9755 test9756 test9757 test9758 test9759 test9760 test9761 test9762 test9763 test9764 test9765 test9766 test9767 test9768 test9769 test9770 test9771 test9772 test9773 test9774 test9775 test9776 test9777 test9778 test9779 test9780 test9781 test9782 test9783 test9784 test9785 test9786 test9787 test9788 test9789 test9790 test9791 test9792 test9793 test9794 test9795 test9796 test9797 test9798 test9799 test9800 test9801 test9802 test9803 test9804 test9805 test9806 test9807 test9808 test9809 test9810 test9811 test9812 test9813 test9814 test9815 test9816 test9817 test9818 test9819 test9820 test9821 test9822 test9823 test9824 test9825 test9826 test9827 test9828 test9829 test9830 test9831 test9832 test9833 test9834 test9835 test9836 test9837 test9838 test9839 test9840 test9841 test9842 test9843 test9844 test9845 test9846 test9847 test9848 test9849 test9850 test9851 test9852 test9853 test9854 test9855 test9856 test9857 test9858 test9859 test9860 test9861 test9862 test9863 test9864 test9865 test9866 test9867 test9868 test9869 test9870 test9871 test9872 test9873 test9874 test9875 test9876 test9877 test9878 test9879 test9880 test9881 test9882 test9883 test9884 test9885 test9886 test9887 test9888 test9889 test9890 test9891 test9892 test9893 test9894 test9895 test9896 test9897 test9898 test9899 test9900 test9901 test9902 test9903 test9904 test9905 test9906 test9907 test9908 test9909 test9910 test9911 test9912 test9913 test9914 test9915 test9916 test9917 test9918 test9919 test9920 test9921 test9922 test9923 test9924 test9925 test9926 test9927 test9928 test9929 test9930 test9931 test9932 test9933 test9934 test9935 test9936 test9937 test9938 test9939 test9940 test9941 test9942 test9943 test9944 test9945 test9946 test9947 test9948 test9949 test9950 test9951 test9952 test9953 test9954 test9955 test9956 test9957 test9958 test9959 test9960 test9961 test9962 test9963 test9964 test9965 test9966 test9967 test9968 test9969 test9970 test9971 test9972 test9973 test9974 test9975 test9976 test9977 test9978 test9979 test9980 test9981 test9982 test9983 test9984 test9985 test9986 test9987 test9988 test9989 test9990 test9991 test9992 test9993 test9994 test9995 test9996 test9997 test9998 test9999 test10000 } } + """ + + test "long query" do + {time, {:ok, _}} = :timer.tc(&Absinthe.Lexer.tokenize/1, [@query]) + IO.puts("long query lexer time was #{time / 1_000_000} sec") + end end From aa37b10900e55e16aa68017a2fa69de875d3817d Mon Sep 17 00:00:00 2001 From: Ellie Poley Date: Thu, 29 Dec 2022 15:59:36 -0600 Subject: [PATCH 054/120] Add some code comments for the review process --- lib/absinthe/lexer.ex | 35 +++++++++++++++++++---------------- 1 file changed, 19 insertions(+), 16 deletions(-) diff --git a/lib/absinthe/lexer.ex b/lib/absinthe/lexer.ex index 7311d2359c..041eeef144 100644 --- a/lib/absinthe/lexer.ex +++ b/lib/absinthe/lexer.ex @@ -243,9 +243,10 @@ defmodule Absinthe.Lexer do end defp optimized_map_token_column(tokens, [first_line | remaining_lines]) do - # IO.inspect(tokens, label: "tokens") - - # lines and chars are 1 indexed not 0 indexed + # RIP me. Lines and columns returned by the tokenizer are 1-indexed. + # The lists and strings we are processing are 0-indexed. + # This means we'll have to start the line number and character/byte offsets at 1, + # but we'll have to adjust the math later when dealing with lists and strings. do_optimized_map_token_column( [], tokens, @@ -257,50 +258,58 @@ defmodule Absinthe.Lexer do ) end + # le base case! if there are no tokens left, return the accumulated results defp do_optimized_map_token_column(results, [] = _tokens, _, _, _, _, _), do: results + # le recursive case defp do_optimized_map_token_column( + # Accumulator. Tokens with locations adjusted from bytes to chars. Ex: [{:foo, {1, 2}}, {:bar, {2, 1}}] results, + # The raw tokens (will take one off the front of the list at each step) [current_token | rest_tokens], + # Which line number we're currently working on line_num, + # The line string that we're currently working on line_part, + # The list of lines that come after the current line (remove them as we process) remaining_lines, + # For the current line, how many characters we've counted so far. char_offset, + # For the current line, how many bytes we've counted so far byte_offset ) do - # extract byte loc + # extract byte loc (gotta handle two token formats) {token_line_num, token_byte_col} = case current_token do {_, byte_location, _} -> byte_location {_, byte_location} -> byte_location end - # |> IO.inspect(label: "token") - # update the current line cursor if we need to move to the next line {current_line_num, current_line, remaining_lines, char_offset, byte_offset} = cond do token_line_num > line_num -> adjust_lines_cursor(remaining_lines, token_line_num, line_num) - # |> IO.inspect(label: "gt") - token_line_num == line_num -> {line_num, line_part, remaining_lines, char_offset, byte_offset} - # |> IO.inspect(label: "eq") end + # count the characters leading up to current token's column, and add the previous offset adjusted_byte_col = token_byte_col - byte_offset partial_byte_prefix = binary_part(current_line, 0, adjusted_byte_col) char_col = String.length(partial_byte_prefix) + char_offset - # byte_size is constant time!! https://hexdocs.pm/elixir/1.12/Kernel.html#byte_size/1 + # prepare data for the next recursive call: + # cut that token off the front of the line string (removing what we've already processed) next_line_part = binary_part(current_line, adjusted_byte_col, byte_size(current_line) - adjusted_byte_col) + # get the next offsets ready next_byte_offset = token_byte_col next_char_offset = char_col + # gotta handle two token formats result = case current_token do {ident, _, data} -> {ident, {token_line_num, char_col}, data} @@ -309,8 +318,6 @@ defmodule Absinthe.Lexer do results = results ++ [result] - # IO.inspect("---") - do_optimized_map_token_column( results, rest_tokens, @@ -322,11 +329,7 @@ defmodule Absinthe.Lexer do ) end - # refactor inline? defp adjust_lines_cursor(lines, desired_line_num, current_line_num) do - # IO.inspect(lines, label: "lines") - # IO.inspect(desired_line_num, label: "desired") - # IO.inspect(current_line_num, label: "current") {_discarded, next_lines} = Enum.split(lines, desired_line_num - current_line_num - 1) [current_line | remaining_lines] = next_lines {desired_line_num, current_line, remaining_lines, 1, 1} From 571d537e1ae934554e6053218a86e041f9bd7d49 Mon Sep 17 00:00:00 2001 From: Ellie Poley Date: Thu, 29 Dec 2022 17:49:49 -0600 Subject: [PATCH 055/120] Comments and notes from review --- lib/absinthe/lexer.ex | 41 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) diff --git a/lib/absinthe/lexer.ex b/lib/absinthe/lexer.ex index 041eeef144..b1efd325a3 100644 --- a/lib/absinthe/lexer.ex +++ b/lib/absinthe/lexer.ex @@ -248,12 +248,19 @@ defmodule Absinthe.Lexer do # This means we'll have to start the line number and character/byte offsets at 1, # but we'll have to adjust the math later when dealing with lists and strings. do_optimized_map_token_column( + # results [], + # list of tokens, we'll reduce as we go tokens, + # current line number 1, + # current line string / substring first_line, + # the lines after the current line remaining_lines, + # character offset 1, + # byte offset 1 ) end @@ -285,6 +292,11 @@ defmodule Absinthe.Lexer do {_, byte_location} -> byte_location end + # Zoe idea: potentially move condition into adjust_lines_cursor fn to simplify this? + # Repetition of variables is confusing + # Maybe put all the cursor info in a map and pass it around to update it? + # Could help with top recursive function and initializing a gajillion arguments + # update the current line cursor if we need to move to the next line {current_line_num, current_line, remaining_lines, char_offset, byte_offset} = cond do @@ -296,6 +308,35 @@ defmodule Absinthe.Lexer do end # count the characters leading up to current token's column, and add the previous offset + + # "b🦕r baz x" + # #byte + # [ + # {:b🦕r, {1, 1}}, + # {:baz, {1, 5}}, + # {:x, {1, 9}} + # ] + + # # 1st: b🦕r + # current_line = "b🦕r baz x" + # adjusted_byte_col = 1 - 1 + # partial_byte_prefix = "" + # char_col = 0 + 1 + + # # 2nd: baz + # current_line = "b🦕r baz x" + # adjusted_byte_col = 6 - 1 + # partial_byte_prefix = "b🦕r " + # char_col = 4 + 1 == 5 + + # #3rd: x --- byte offset is 6 and char offset is 5 + # current_line = "baz x" + # adjusted_byte_col = 10 - 6 == 4 + # partial_byte_prefix = "baz " + # char_col = 4 + 5 == 9' + + # Code review note... it's confusing that current_line has to include the previous token, is there something to make this clearer? + adjusted_byte_col = token_byte_col - byte_offset partial_byte_prefix = binary_part(current_line, 0, adjusted_byte_col) char_col = String.length(partial_byte_prefix) + char_offset From b7baf0ef80a35de587654d372ec812aeca52c790 Mon Sep 17 00:00:00 2001 From: Ellie Poley Date: Thu, 29 Dec 2022 18:24:38 -0600 Subject: [PATCH 056/120] Refactor for clarity --- lib/absinthe/lexer.ex | 160 +++++++++++++++++++----------------------- 1 file changed, 73 insertions(+), 87 deletions(-) diff --git a/lib/absinthe/lexer.ex b/lib/absinthe/lexer.ex index b1efd325a3..36d25696a5 100644 --- a/lib/absinthe/lexer.ex +++ b/lib/absinthe/lexer.ex @@ -242,31 +242,32 @@ defmodule Absinthe.Lexer do end end - defp optimized_map_token_column(tokens, [first_line | remaining_lines]) do + defp optimized_map_token_column(tokens, [first_line | next_lines]) do # RIP me. Lines and columns returned by the tokenizer are 1-indexed. # The lists and strings we are processing are 0-indexed. # This means we'll have to start the line number and character/byte offsets at 1, # but we'll have to adjust the math later when dealing with lists and strings. + + initial_cursor = %{ + line_num_cursor: 1, + current_line_substring: first_line, + current_line_char_offset: 1, + current_line_byte_offset: 1, + next_lines: next_lines + } + do_optimized_map_token_column( # results [], # list of tokens, we'll reduce as we go tokens, - # current line number - 1, - # current line string / substring - first_line, - # the lines after the current line - remaining_lines, - # character offset - 1, - # byte offset - 1 + # initialized cursor state to start at the beginning of the first line + initial_cursor ) end # le base case! if there are no tokens left, return the accumulated results - defp do_optimized_map_token_column(results, [] = _tokens, _, _, _, _, _), do: results + defp do_optimized_map_token_column(results, [] = _tokens, _cursor), do: results # le recursive case defp do_optimized_map_token_column( @@ -274,87 +275,56 @@ defmodule Absinthe.Lexer do results, # The raw tokens (will take one off the front of the list at each step) [current_token | rest_tokens], - # Which line number we're currently working on - line_num, - # The line string that we're currently working on - line_part, - # The list of lines that come after the current line (remove them as we process) - remaining_lines, - # For the current line, how many characters we've counted so far. - char_offset, - # For the current line, how many bytes we've counted so far - byte_offset + # Cursor data to track where we are in processing currently + cursor ) do # extract byte loc (gotta handle two token formats) - {token_line_num, token_byte_col} = + {raw_token_line_num, raw_token_byte_col} = case current_token do {_, byte_location, _} -> byte_location {_, byte_location} -> byte_location end - # Zoe idea: potentially move condition into adjust_lines_cursor fn to simplify this? - # Repetition of variables is confusing - # Maybe put all the cursor info in a map and pass it around to update it? - # Could help with top recursive function and initializing a gajillion arguments - # update the current line cursor if we need to move to the next line - {current_line_num, current_line, remaining_lines, char_offset, byte_offset} = - cond do - token_line_num > line_num -> - adjust_lines_cursor(remaining_lines, token_line_num, line_num) - - token_line_num == line_num -> - {line_num, line_part, remaining_lines, char_offset, byte_offset} - end + %{ + # Which line number we're currently working on + line_num_cursor: _line_num_cursor, + # The line substring that we're currently working on + current_line_substring: current_line_substring, + # For the current line, how many characters we've counted so far. + current_line_char_offset: current_line_char_offset, + # For the current line, how many bytes we've counted so far + current_line_byte_offset: current_line_byte_offset, + # The list of lines that come after the current line (we remove them as we process) + next_lines: _next_lines + } = cursor = maybe_move_cursor_to_next_line(cursor, raw_token_line_num) # count the characters leading up to current token's column, and add the previous offset - - # "b🦕r baz x" - # #byte - # [ - # {:b🦕r, {1, 1}}, - # {:baz, {1, 5}}, - # {:x, {1, 9}} - # ] - - # # 1st: b🦕r - # current_line = "b🦕r baz x" - # adjusted_byte_col = 1 - 1 - # partial_byte_prefix = "" - # char_col = 0 + 1 - - # # 2nd: baz - # current_line = "b🦕r baz x" - # adjusted_byte_col = 6 - 1 - # partial_byte_prefix = "b🦕r " - # char_col = 4 + 1 == 5 - - # #3rd: x --- byte offset is 6 and char offset is 5 - # current_line = "baz x" - # adjusted_byte_col = 10 - 6 == 4 - # partial_byte_prefix = "baz " - # char_col = 4 + 5 == 9' - # Code review note... it's confusing that current_line has to include the previous token, is there something to make this clearer? - - adjusted_byte_col = token_byte_col - byte_offset - partial_byte_prefix = binary_part(current_line, 0, adjusted_byte_col) - char_col = String.length(partial_byte_prefix) + char_offset + adjusted_byte_col = raw_token_byte_col - current_line_byte_offset + token_partial_prefix = binary_part(current_line_substring, 0, adjusted_byte_col) + char_col = String.length(token_partial_prefix) + current_line_char_offset # prepare data for the next recursive call: - # cut that token off the front of the line string (removing what we've already processed) - next_line_part = - binary_part(current_line, adjusted_byte_col, byte_size(current_line) - adjusted_byte_col) - - # get the next offsets ready - next_byte_offset = token_byte_col - next_char_offset = char_col - - # gotta handle two token formats + # cut the previous token token off the front of the line string (removing what we've already processed) + updated_line_substring = + binary_part( + current_line_substring, + adjusted_byte_col, + byte_size(current_line_substring) - adjusted_byte_col + ) + + next_cursor = + cursor + |> Map.put(:current_line_substring, updated_line_substring) + |> Map.put(:current_line_byte_offset, raw_token_byte_col) + |> Map.put(:current_line_char_offset, char_col) + + # gotta handle two token formats to make the result result = case current_token do - {ident, _, data} -> {ident, {token_line_num, char_col}, data} - {ident, _} -> {ident, {token_line_num, char_col}} + {ident, _, data} -> {ident, {raw_token_line_num, char_col}, data} + {ident, _} -> {ident, {raw_token_line_num, char_col}} end results = results ++ [result] @@ -362,18 +332,34 @@ defmodule Absinthe.Lexer do do_optimized_map_token_column( results, rest_tokens, - current_line_num, - next_line_part, - remaining_lines, - next_char_offset, - next_byte_offset + next_cursor ) end - defp adjust_lines_cursor(lines, desired_line_num, current_line_num) do - {_discarded, next_lines} = Enum.split(lines, desired_line_num - current_line_num - 1) - [current_line | remaining_lines] = next_lines - {desired_line_num, current_line, remaining_lines, 1, 1} + defp maybe_move_cursor_to_next_line( + %{line_num_cursor: line_num_cursor} = cursor, + desired_line_num + ) + when desired_line_num == line_num_cursor, + do: cursor + + defp maybe_move_cursor_to_next_line( + %{line_num_cursor: line_num_cursor, next_lines: next_lines} = _cursor, + desired_line_num + ) + when line_num_cursor < desired_line_num do + {_completed, unprocessed_lines} = + Enum.split(next_lines, desired_line_num - line_num_cursor - 1) + + [current_line | next_lines] = unprocessed_lines + + %{ + line_num_cursor: desired_line_num, + current_line_substring: current_line, + current_line_char_offset: 1, + current_line_byte_offset: 1, + next_lines: next_lines + } end defp byte_loc_to_char_loc({line, byte_col}, lines) do From 2b58b83b4b1eeba28073be27380cd69f39d37584 Mon Sep 17 00:00:00 2001 From: Ellie Poley Date: Fri, 30 Dec 2022 12:45:09 -0600 Subject: [PATCH 057/120] More refactoring for clarity --- lib/absinthe/lexer.ex | 152 ++++++++++++++++++------------------------ 1 file changed, 64 insertions(+), 88 deletions(-) diff --git a/lib/absinthe/lexer.ex b/lib/absinthe/lexer.ex index 36d25696a5..8aafa65596 100644 --- a/lib/absinthe/lexer.ex +++ b/lib/absinthe/lexer.ex @@ -233,7 +233,7 @@ defmodule Absinthe.Lexer do case do_tokenize(input) do {:ok, tokens, "", _, _, _} -> - tokens = optimized_map_token_column(tokens, lines) + tokens = optimized_convert_token_columns(tokens, lines) {:ok, tokens} {:ok, _, rest, _, {line, line_offset}, byte_offset} -> @@ -242,12 +242,7 @@ defmodule Absinthe.Lexer do end end - defp optimized_map_token_column(tokens, [first_line | next_lines]) do - # RIP me. Lines and columns returned by the tokenizer are 1-indexed. - # The lists and strings we are processing are 0-indexed. - # This means we'll have to start the line number and character/byte offsets at 1, - # but we'll have to adjust the math later when dealing with lists and strings. - + defp optimized_convert_token_columns(tokens, [first_line | next_lines]) do initial_cursor = %{ line_num_cursor: 1, current_line_substring: first_line, @@ -256,98 +251,79 @@ defmodule Absinthe.Lexer do next_lines: next_lines } - do_optimized_map_token_column( - # results - [], - # list of tokens, we'll reduce as we go - tokens, - # initialized cursor state to start at the beginning of the first line - initial_cursor - ) + Enum.reduce(tokens, {[], initial_cursor}, fn current_token, {results, cursor} -> + cursor = maybe_move_cursor_to_next_line(cursor, current_token) + + token_byte_col = + case current_token do + {_, {_, byte_col}, _} -> byte_col + {_, {_, byte_col}} -> byte_col + end + + adjusted_byte_col = token_byte_col - cursor.current_line_byte_offset + token_partial_prefix = binary_part(cursor.current_line_substring, 0, adjusted_byte_col) + token_char_col = String.length(token_partial_prefix) + cursor.current_line_char_offset + + updated_line_substring = + binary_part( + cursor.current_line_substring, + adjusted_byte_col, + byte_size(cursor.current_line_substring) - adjusted_byte_col + ) + + next_cursor = + cursor + |> Map.put(:current_line_substring, updated_line_substring) + |> Map.put(:current_line_byte_offset, token_byte_col) + |> Map.put(:current_line_char_offset, token_char_col) + + result = + case current_token do + {ident, {line_num, _}, data} -> {ident, {line_num, token_char_col}, data} + {ident, {line_num, _}} -> {ident, {line_num, token_char_col}} + end + + results = results ++ [result] + + {results, next_cursor} + end) + |> case do + {results, _} -> results + end end - # le base case! if there are no tokens left, return the accumulated results - defp do_optimized_map_token_column(results, [] = _tokens, _cursor), do: results - - # le recursive case - defp do_optimized_map_token_column( - # Accumulator. Tokens with locations adjusted from bytes to chars. Ex: [{:foo, {1, 2}}, {:bar, {2, 1}}] - results, - # The raw tokens (will take one off the front of the list at each step) - [current_token | rest_tokens], - # Cursor data to track where we are in processing currently - cursor - ) do - # extract byte loc (gotta handle two token formats) - {raw_token_line_num, raw_token_byte_col} = - case current_token do - {_, byte_location, _} -> byte_location - {_, byte_location} -> byte_location - end - - # update the current line cursor if we need to move to the next line - %{ - # Which line number we're currently working on - line_num_cursor: _line_num_cursor, - # The line substring that we're currently working on - current_line_substring: current_line_substring, - # For the current line, how many characters we've counted so far. - current_line_char_offset: current_line_char_offset, - # For the current line, how many bytes we've counted so far - current_line_byte_offset: current_line_byte_offset, - # The list of lines that come after the current line (we remove them as we process) - next_lines: _next_lines - } = cursor = maybe_move_cursor_to_next_line(cursor, raw_token_line_num) - - # count the characters leading up to current token's column, and add the previous offset - # Code review note... it's confusing that current_line has to include the previous token, is there something to make this clearer? - adjusted_byte_col = raw_token_byte_col - current_line_byte_offset - token_partial_prefix = binary_part(current_line_substring, 0, adjusted_byte_col) - char_col = String.length(token_partial_prefix) + current_line_char_offset - - # prepare data for the next recursive call: - # cut the previous token token off the front of the line string (removing what we've already processed) - updated_line_substring = - binary_part( - current_line_substring, - adjusted_byte_col, - byte_size(current_line_substring) - adjusted_byte_col - ) - - next_cursor = - cursor - |> Map.put(:current_line_substring, updated_line_substring) - |> Map.put(:current_line_byte_offset, raw_token_byte_col) - |> Map.put(:current_line_char_offset, char_col) - - # gotta handle two token formats to make the result - result = - case current_token do - {ident, _, data} -> {ident, {raw_token_line_num, char_col}, data} - {ident, _} -> {ident, {raw_token_line_num, char_col}} - end - - results = results ++ [result] - - do_optimized_map_token_column( - results, - rest_tokens, - next_cursor - ) - end + defp maybe_move_cursor_to_next_line( + %{line_num_cursor: line_num_cursor} = cursor, + {_, {token_line, _}, _} + ) + when token_line == line_num_cursor, + do: cursor defp maybe_move_cursor_to_next_line( %{line_num_cursor: line_num_cursor} = cursor, - desired_line_num + {_, {token_line, _}} ) - when desired_line_num == line_num_cursor, + when token_line == line_num_cursor, do: cursor defp maybe_move_cursor_to_next_line( + %{line_num_cursor: line_num_cursor} = cursor, + {_, {token_line, _}} + ) + when line_num_cursor < token_line, + do: move_cursor_to_next_line(cursor, token_line) + + defp maybe_move_cursor_to_next_line( + %{line_num_cursor: line_num_cursor} = cursor, + {_, {token_line, _}, _} + ) + when line_num_cursor < token_line, + do: move_cursor_to_next_line(cursor, token_line) + + defp move_cursor_to_next_line( %{line_num_cursor: line_num_cursor, next_lines: next_lines} = _cursor, desired_line_num - ) - when line_num_cursor < desired_line_num do + ) do {_completed, unprocessed_lines} = Enum.split(next_lines, desired_line_num - line_num_cursor - 1) From 730c84c8fc8ed886af0121dcba8845037f58ac5e Mon Sep 17 00:00:00 2001 From: Ellie Poley Date: Wed, 4 Jan 2023 17:56:57 -0600 Subject: [PATCH 058/120] Refactor slightly per code review feedback. Also optimized result accumulation --- lib/absinthe/lexer.ex | 86 +++++++++++++++++++------------------------ 1 file changed, 37 insertions(+), 49 deletions(-) diff --git a/lib/absinthe/lexer.ex b/lib/absinthe/lexer.ex index 8aafa65596..80f1bfedde 100644 --- a/lib/absinthe/lexer.ex +++ b/lib/absinthe/lexer.ex @@ -233,7 +233,7 @@ defmodule Absinthe.Lexer do case do_tokenize(input) do {:ok, tokens, "", _, _, _} -> - tokens = optimized_convert_token_columns(tokens, lines) + tokens = convert_token_columns_from_byte_to_char(tokens, lines) {:ok, tokens} {:ok, _, rest, _, {line, line_offset}, byte_offset} -> @@ -242,8 +242,8 @@ defmodule Absinthe.Lexer do end end - defp optimized_convert_token_columns(tokens, [first_line | next_lines]) do - initial_cursor = %{ + defp convert_token_columns_from_byte_to_char(tokens, [first_line | next_lines]) do + initial_cursor_state = %{ line_num_cursor: 1, current_line_substring: first_line, current_line_char_offset: 1, @@ -251,86 +251,74 @@ defmodule Absinthe.Lexer do next_lines: next_lines } - Enum.reduce(tokens, {[], initial_cursor}, fn current_token, {results, cursor} -> - cursor = maybe_move_cursor_to_next_line(cursor, current_token) - - token_byte_col = + Enum.reduce(tokens, {[], initial_cursor_state}, fn current_token, {results, cursor_state} -> + {token_line_num, token_byte_col} = case current_token do - {_, {_, byte_col}, _} -> byte_col - {_, {_, byte_col}} -> byte_col + {_, {token_line_num, token_byte_col}, _} -> {token_line_num, token_byte_col} + {_, {token_line_num, token_byte_col}} -> {token_line_num, token_byte_col} end - adjusted_byte_col = token_byte_col - cursor.current_line_byte_offset - token_partial_prefix = binary_part(cursor.current_line_substring, 0, adjusted_byte_col) - token_char_col = String.length(token_partial_prefix) + cursor.current_line_char_offset + cursor_state = maybe_move_cursor_to_next_line(cursor_state, token_line_num) + + adjusted_byte_col = token_byte_col - cursor_state.current_line_byte_offset + + line_part_from_prev_to_current_token = + binary_part(cursor_state.current_line_substring, 0, adjusted_byte_col) + + token_char_col = + String.length(line_part_from_prev_to_current_token) + + cursor_state.current_line_char_offset updated_line_substring = binary_part( - cursor.current_line_substring, + cursor_state.current_line_substring, adjusted_byte_col, - byte_size(cursor.current_line_substring) - adjusted_byte_col + byte_size(cursor_state.current_line_substring) - adjusted_byte_col ) - next_cursor = - cursor + next_cursor_state = + cursor_state |> Map.put(:current_line_substring, updated_line_substring) |> Map.put(:current_line_byte_offset, token_byte_col) |> Map.put(:current_line_char_offset, token_char_col) result = case current_token do - {ident, {line_num, _}, data} -> {ident, {line_num, token_char_col}, data} - {ident, {line_num, _}} -> {ident, {line_num, token_char_col}} + {ident, _, data} -> {ident, {token_line_num, token_char_col}, data} + {ident, _} -> {ident, {token_line_num, token_char_col}} end - results = results ++ [result] - - {results, next_cursor} + {[result | results], next_cursor_state} end) |> case do - {results, _} -> results + {results, _} -> Enum.reverse(results) end end defp maybe_move_cursor_to_next_line( - %{line_num_cursor: line_num_cursor} = cursor, - {_, {token_line, _}, _} - ) - when token_line == line_num_cursor, - do: cursor - - defp maybe_move_cursor_to_next_line( - %{line_num_cursor: line_num_cursor} = cursor, - {_, {token_line, _}} - ) - when token_line == line_num_cursor, - do: cursor - - defp maybe_move_cursor_to_next_line( - %{line_num_cursor: line_num_cursor} = cursor, - {_, {token_line, _}} + %{line_num_cursor: line_num_cursor} = cursor_state, + token_line_num ) - when line_num_cursor < token_line, - do: move_cursor_to_next_line(cursor, token_line) + when token_line_num == line_num_cursor, + do: cursor_state defp maybe_move_cursor_to_next_line( - %{line_num_cursor: line_num_cursor} = cursor, - {_, {token_line, _}, _} + %{line_num_cursor: line_num_cursor} = cursor_state, + token_line_num ) - when line_num_cursor < token_line, - do: move_cursor_to_next_line(cursor, token_line) + when line_num_cursor < token_line_num, + do: move_cursor_to_next_line(cursor_state, token_line_num) defp move_cursor_to_next_line( - %{line_num_cursor: line_num_cursor, next_lines: next_lines} = _cursor, - desired_line_num + %{line_num_cursor: line_num_cursor, next_lines: next_lines} = _cursor_state, + token_line_num ) do - {_completed, unprocessed_lines} = - Enum.split(next_lines, desired_line_num - line_num_cursor - 1) + {_completed, unprocessed_lines} = Enum.split(next_lines, token_line_num - line_num_cursor - 1) [current_line | next_lines] = unprocessed_lines %{ - line_num_cursor: desired_line_num, + line_num_cursor: token_line_num, current_line_substring: current_line, current_line_char_offset: 1, current_line_byte_offset: 1, From 2d0a0c067d81e473d09cb0c135936e68d6c67b00 Mon Sep 17 00:00:00 2001 From: Ellie Poley Date: Wed, 4 Jan 2023 18:06:49 -0600 Subject: [PATCH 059/120] formalize test --- test/absinthe/lexer_test.exs | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/test/absinthe/lexer_test.exs b/test/absinthe/lexer_test.exs index 045aa6d2ec..8a8961a6c5 100644 --- a/test/absinthe/lexer_test.exs +++ b/test/absinthe/lexer_test.exs @@ -95,12 +95,9 @@ defmodule Absinthe.LexerTest do ]} == Absinthe.Lexer.tokenize(@query) end - @query """ - { test1 { test2 test3 test4 test5 test6 test7 test8 test9 test10 test11 test12 test13 test14 test15 test16 test17 test18 test19 test20 test21 test22 test23 test24 test25 test26 test27 test28 test29 test30 test31 test32 test33 test34 test35 test36 test37 test38 test39 test40 test41 test42 test43 test44 test45 test46 test47 test48 test49 test50 test51 test52 test53 test54 test55 test56 test57 test58 test59 test60 test61 test62 test63 test64 test65 test66 test67 test68 test69 test70 test71 test72 test73 test74 test75 test76 test77 test78 test79 test80 test81 test82 test83 test84 test85 test86 test87 test88 test89 test90 test91 test92 test93 test94 test95 test96 test97 test98 test99 test100 test101 test102 test103 test104 test105 test106 test107 test108 test109 test110 test111 test112 test113 test114 test115 test116 test117 test118 test119 test120 test121 test122 test123 test124 test125 test126 test127 test128 test129 test130 test131 test132 test133 test134 test135 test136 test137 test138 test139 test140 test141 test142 test143 test144 test145 test146 test147 test148 test149 test150 test151 test152 test153 test154 test155 test156 test157 test158 test159 test160 test161 test162 test163 test164 test165 test166 test167 test168 test169 test170 test171 test172 test173 test174 test175 test176 test177 test178 test179 test180 test181 test182 test183 test184 test185 test186 test187 test188 test189 test190 test191 test192 test193 test194 test195 test196 test197 test198 test199 test200 test201 test202 test203 test204 test205 test206 test207 test208 test209 test210 test211 test212 test213 test214 test215 test216 test217 test218 test219 test220 test221 test222 test223 test224 test225 test226 test227 test228 test229 test230 test231 test232 test233 test234 test235 test236 test237 test238 test239 test240 test241 test242 test243 test244 test245 test246 test247 test248 test249 test250 test251 test252 test253 test254 test255 test256 test257 test258 test259 test260 test261 test262 test263 test264 test265 test266 test267 test268 test269 test270 test271 test272 test273 test274 test275 test276 test277 test278 test279 test280 test281 test282 test283 test284 test285 test286 test287 test288 test289 test290 test291 test292 test293 test294 test295 test296 test297 test298 test299 test300 test301 test302 test303 test304 test305 test306 test307 test308 test309 test310 test311 test312 test313 test314 test315 test316 test317 test318 test319 test320 test321 test322 test323 test324 test325 test326 test327 test328 test329 test330 test331 test332 test333 test334 test335 test336 test337 test338 test339 test340 test341 test342 test343 test344 test345 test346 test347 test348 test349 test350 test351 test352 test353 test354 test355 test356 test357 test358 test359 test360 test361 test362 test363 test364 test365 test366 test367 test368 test369 test370 test371 test372 test373 test374 test375 test376 test377 test378 test379 test380 test381 test382 test383 test384 test385 test386 test387 test388 test389 test390 test391 test392 test393 test394 test395 test396 test397 test398 test399 test400 test401 test402 test403 test404 test405 test406 test407 test408 test409 test410 test411 test412 test413 test414 test415 test416 test417 test418 test419 test420 test421 test422 test423 test424 test425 test426 test427 test428 test429 test430 test431 test432 test433 test434 test435 test436 test437 test438 test439 test440 test441 test442 test443 test444 test445 test446 test447 test448 test449 test450 test451 test452 test453 test454 test455 test456 test457 test458 test459 test460 test461 test462 test463 test464 test465 test466 test467 test468 test469 test470 test471 test472 test473 test474 test475 test476 test477 test478 test479 test480 test481 test482 test483 test484 test485 test486 test487 test488 test489 test490 test491 test492 test493 test494 test495 test496 test497 test498 test499 test500 test501 test502 test503 test504 test505 test506 test507 test508 test509 test510 test511 test512 test513 test514 test515 test516 test517 test518 test519 test520 test521 test522 test523 test524 test525 test526 test527 test528 test529 test530 test531 test532 test533 test534 test535 test536 test537 test538 test539 test540 test541 test542 test543 test544 test545 test546 test547 test548 test549 test550 test551 test552 test553 test554 test555 test556 test557 test558 test559 test560 test561 test562 test563 test564 test565 test566 test567 test568 test569 test570 test571 test572 test573 test574 test575 test576 test577 test578 test579 test580 test581 test582 test583 test584 test585 test586 test587 test588 test589 test590 test591 test592 test593 test594 test595 test596 test597 test598 test599 test600 test601 test602 test603 test604 test605 test606 test607 test608 test609 test610 test611 test612 test613 test614 test615 test616 test617 test618 test619 test620 test621 test622 test623 test624 test625 test626 test627 test628 test629 test630 test631 test632 test633 test634 test635 test636 test637 test638 test639 test640 test641 test642 test643 test644 test645 test646 test647 test648 test649 test650 test651 test652 test653 test654 test655 test656 test657 test658 test659 test660 test661 test662 test663 test664 test665 test666 test667 test668 test669 test670 test671 test672 test673 test674 test675 test676 test677 test678 test679 test680 test681 test682 test683 test684 test685 test686 test687 test688 test689 test690 test691 test692 test693 test694 test695 test696 test697 test698 test699 test700 test701 test702 test703 test704 test705 test706 test707 test708 test709 test710 test711 test712 test713 test714 test715 test716 test717 test718 test719 test720 test721 test722 test723 test724 test725 test726 test727 test728 test729 test730 test731 test732 test733 test734 test735 test736 test737 test738 test739 test740 test741 test742 test743 test744 test745 test746 test747 test748 test749 test750 test751 test752 test753 test754 test755 test756 test757 test758 test759 test760 test761 test762 test763 test764 test765 test766 test767 test768 test769 test770 test771 test772 test773 test774 test775 test776 test777 test778 test779 test780 test781 test782 test783 test784 test785 test786 test787 test788 test789 test790 test791 test792 test793 test794 test795 test796 test797 test798 test799 test800 test801 test802 test803 test804 test805 test806 test807 test808 test809 test810 test811 test812 test813 test814 test815 test816 test817 test818 test819 test820 test821 test822 test823 test824 test825 test826 test827 test828 test829 test830 test831 test832 test833 test834 test835 test836 test837 test838 test839 test840 test841 test842 test843 test844 test845 test846 test847 test848 test849 test850 test851 test852 test853 test854 test855 test856 test857 test858 test859 test860 test861 test862 test863 test864 test865 test866 test867 test868 test869 test870 test871 test872 test873 test874 test875 test876 test877 test878 test879 test880 test881 test882 test883 test884 test885 test886 test887 test888 test889 test890 test891 test892 test893 test894 test895 test896 test897 test898 test899 test900 test901 test902 test903 test904 test905 test906 test907 test908 test909 test910 test911 test912 test913 test914 test915 test916 test917 test918 test919 test920 test921 test922 test923 test924 test925 test926 test927 test928 test929 test930 test931 test932 test933 test934 test935 test936 test937 test938 test939 test940 test941 test942 test943 test944 test945 test946 test947 test948 test949 test950 test951 test952 test953 test954 test955 test956 test957 test958 test959 test960 test961 test962 test963 test964 test965 test966 test967 test968 test969 test970 test971 test972 test973 test974 test975 test976 test977 test978 test979 test980 test981 test982 test983 test984 test985 test986 test987 test988 test989 test990 test991 test992 test993 test994 test995 test996 test997 test998 test999 test1000 test1001 test1002 test1003 test1004 test1005 test1006 test1007 test1008 test1009 test1010 test1011 test1012 test1013 test1014 test1015 test1016 test1017 test1018 test1019 test1020 test1021 test1022 test1023 test1024 test1025 test1026 test1027 test1028 test1029 test1030 test1031 test1032 test1033 test1034 test1035 test1036 test1037 test1038 test1039 test1040 test1041 test1042 test1043 test1044 test1045 test1046 test1047 test1048 test1049 test1050 test1051 test1052 test1053 test1054 test1055 test1056 test1057 test1058 test1059 test1060 test1061 test1062 test1063 test1064 test1065 test1066 test1067 test1068 test1069 test1070 test1071 test1072 test1073 test1074 test1075 test1076 test1077 test1078 test1079 test1080 test1081 test1082 test1083 test1084 test1085 test1086 test1087 test1088 test1089 test1090 test1091 test1092 test1093 test1094 test1095 test1096 test1097 test1098 test1099 test1100 test1101 test1102 test1103 test1104 test1105 test1106 test1107 test1108 test1109 test1110 test1111 test1112 test1113 test1114 test1115 test1116 test1117 test1118 test1119 test1120 test1121 test1122 test1123 test1124 test1125 test1126 test1127 test1128 test1129 test1130 test1131 test1132 test1133 test1134 test1135 test1136 test1137 test1138 test1139 test1140 test1141 test1142 test1143 test1144 test1145 test1146 test1147 test1148 test1149 test1150 test1151 test1152 test1153 test1154 test1155 test1156 test1157 test1158 test1159 test1160 test1161 test1162 test1163 test1164 test1165 test1166 test1167 test1168 test1169 test1170 test1171 test1172 test1173 test1174 test1175 test1176 test1177 test1178 test1179 test1180 test1181 test1182 test1183 test1184 test1185 test1186 test1187 test1188 test1189 test1190 test1191 test1192 test1193 test1194 test1195 test1196 test1197 test1198 test1199 test1200 test1201 test1202 test1203 test1204 test1205 test1206 test1207 test1208 test1209 test1210 test1211 test1212 test1213 test1214 test1215 test1216 test1217 test1218 test1219 test1220 test1221 test1222 test1223 test1224 test1225 test1226 test1227 test1228 test1229 test1230 test1231 test1232 test1233 test1234 test1235 test1236 test1237 test1238 test1239 test1240 test1241 test1242 test1243 test1244 test1245 test1246 test1247 test1248 test1249 test1250 test1251 test1252 test1253 test1254 test1255 test1256 test1257 test1258 test1259 test1260 test1261 test1262 test1263 test1264 test1265 test1266 test1267 test1268 test1269 test1270 test1271 test1272 test1273 test1274 test1275 test1276 test1277 test1278 test1279 test1280 test1281 test1282 test1283 test1284 test1285 test1286 test1287 test1288 test1289 test1290 test1291 test1292 test1293 test1294 test1295 test1296 test1297 test1298 test1299 test1300 test1301 test1302 test1303 test1304 test1305 test1306 test1307 test1308 test1309 test1310 test1311 test1312 test1313 test1314 test1315 test1316 test1317 test1318 test1319 test1320 test1321 test1322 test1323 test1324 test1325 test1326 test1327 test1328 test1329 test1330 test1331 test1332 test1333 test1334 test1335 test1336 test1337 test1338 test1339 test1340 test1341 test1342 test1343 test1344 test1345 test1346 test1347 test1348 test1349 test1350 test1351 test1352 test1353 test1354 test1355 test1356 test1357 test1358 test1359 test1360 test1361 test1362 test1363 test1364 test1365 test1366 test1367 test1368 test1369 test1370 test1371 test1372 test1373 test1374 test1375 test1376 test1377 test1378 test1379 test1380 test1381 test1382 test1383 test1384 test1385 test1386 test1387 test1388 test1389 test1390 test1391 test1392 test1393 test1394 test1395 test1396 test1397 test1398 test1399 test1400 test1401 test1402 test1403 test1404 test1405 test1406 test1407 test1408 test1409 test1410 test1411 test1412 test1413 test1414 test1415 test1416 test1417 test1418 test1419 test1420 test1421 test1422 test1423 test1424 test1425 test1426 test1427 test1428 test1429 test1430 test1431 test1432 test1433 test1434 test1435 test1436 test1437 test1438 test1439 test1440 test1441 test1442 test1443 test1444 test1445 test1446 test1447 test1448 test1449 test1450 test1451 test1452 test1453 test1454 test1455 test1456 test1457 test1458 test1459 test1460 test1461 test1462 test1463 test1464 test1465 test1466 test1467 test1468 test1469 test1470 test1471 test1472 test1473 test1474 test1475 test1476 test1477 test1478 test1479 test1480 test1481 test1482 test1483 test1484 test1485 test1486 test1487 test1488 test1489 test1490 test1491 test1492 test1493 test1494 test1495 test1496 test1497 test1498 test1499 test1500 test1501 test1502 test1503 test1504 test1505 test1506 test1507 test1508 test1509 test1510 test1511 test1512 test1513 test1514 test1515 test1516 test1517 test1518 test1519 test1520 test1521 test1522 test1523 test1524 test1525 test1526 test1527 test1528 test1529 test1530 test1531 test1532 test1533 test1534 test1535 test1536 test1537 test1538 test1539 test1540 test1541 test1542 test1543 test1544 test1545 test1546 test1547 test1548 test1549 test1550 test1551 test1552 test1553 test1554 test1555 test1556 test1557 test1558 test1559 test1560 test1561 test1562 test1563 test1564 test1565 test1566 test1567 test1568 test1569 test1570 test1571 test1572 test1573 test1574 test1575 test1576 test1577 test1578 test1579 test1580 test1581 test1582 test1583 test1584 test1585 test1586 test1587 test1588 test1589 test1590 test1591 test1592 test1593 test1594 test1595 test1596 test1597 test1598 test1599 test1600 test1601 test1602 test1603 test1604 test1605 test1606 test1607 test1608 test1609 test1610 test1611 test1612 test1613 test1614 test1615 test1616 test1617 test1618 test1619 test1620 test1621 test1622 test1623 test1624 test1625 test1626 test1627 test1628 test1629 test1630 test1631 test1632 test1633 test1634 test1635 test1636 test1637 test1638 test1639 test1640 test1641 test1642 test1643 test1644 test1645 test1646 test1647 test1648 test1649 test1650 test1651 test1652 test1653 test1654 test1655 test1656 test1657 test1658 test1659 test1660 test1661 test1662 test1663 test1664 test1665 test1666 test1667 test1668 test1669 test1670 test1671 test1672 test1673 test1674 test1675 test1676 test1677 test1678 test1679 test1680 test1681 test1682 test1683 test1684 test1685 test1686 test1687 test1688 test1689 test1690 test1691 test1692 test1693 test1694 test1695 test1696 test1697 test1698 test1699 test1700 test1701 test1702 test1703 test1704 test1705 test1706 test1707 test1708 test1709 test1710 test1711 test1712 test1713 test1714 test1715 test1716 test1717 test1718 test1719 test1720 test1721 test1722 test1723 test1724 test1725 test1726 test1727 test1728 test1729 test1730 test1731 test1732 test1733 test1734 test1735 test1736 test1737 test1738 test1739 test1740 test1741 test1742 test1743 test1744 test1745 test1746 test1747 test1748 test1749 test1750 test1751 test1752 test1753 test1754 test1755 test1756 test1757 test1758 test1759 test1760 test1761 test1762 test1763 test1764 test1765 test1766 test1767 test1768 test1769 test1770 test1771 test1772 test1773 test1774 test1775 test1776 test1777 test1778 test1779 test1780 test1781 test1782 test1783 test1784 test1785 test1786 test1787 test1788 test1789 test1790 test1791 test1792 test1793 test1794 test1795 test1796 test1797 test1798 test1799 test1800 test1801 test1802 test1803 test1804 test1805 test1806 test1807 test1808 test1809 test1810 test1811 test1812 test1813 test1814 test1815 test1816 test1817 test1818 test1819 test1820 test1821 test1822 test1823 test1824 test1825 test1826 test1827 test1828 test1829 test1830 test1831 test1832 test1833 test1834 test1835 test1836 test1837 test1838 test1839 test1840 test1841 test1842 test1843 test1844 test1845 test1846 test1847 test1848 test1849 test1850 test1851 test1852 test1853 test1854 test1855 test1856 test1857 test1858 test1859 test1860 test1861 test1862 test1863 test1864 test1865 test1866 test1867 test1868 test1869 test1870 test1871 test1872 test1873 test1874 test1875 test1876 test1877 test1878 test1879 test1880 test1881 test1882 test1883 test1884 test1885 test1886 test1887 test1888 test1889 test1890 test1891 test1892 test1893 test1894 test1895 test1896 test1897 test1898 test1899 test1900 test1901 test1902 test1903 test1904 test1905 test1906 test1907 test1908 test1909 test1910 test1911 test1912 test1913 test1914 test1915 test1916 test1917 test1918 test1919 test1920 test1921 test1922 test1923 test1924 test1925 test1926 test1927 test1928 test1929 test1930 test1931 test1932 test1933 test1934 test1935 test1936 test1937 test1938 test1939 test1940 test1941 test1942 test1943 test1944 test1945 test1946 test1947 test1948 test1949 test1950 test1951 test1952 test1953 test1954 test1955 test1956 test1957 test1958 test1959 test1960 test1961 test1962 test1963 test1964 test1965 test1966 test1967 test1968 test1969 test1970 test1971 test1972 test1973 test1974 test1975 test1976 test1977 test1978 test1979 test1980 test1981 test1982 test1983 test1984 test1985 test1986 test1987 test1988 test1989 test1990 test1991 test1992 test1993 test1994 test1995 test1996 test1997 test1998 test1999 test2000 test2001 test2002 test2003 test2004 test2005 test2006 test2007 test2008 test2009 test2010 test2011 test2012 test2013 test2014 test2015 test2016 test2017 test2018 test2019 test2020 test2021 test2022 test2023 test2024 test2025 test2026 test2027 test2028 test2029 test2030 test2031 test2032 test2033 test2034 test2035 test2036 test2037 test2038 test2039 test2040 test2041 test2042 test2043 test2044 test2045 test2046 test2047 test2048 test2049 test2050 test2051 test2052 test2053 test2054 test2055 test2056 test2057 test2058 test2059 test2060 test2061 test2062 test2063 test2064 test2065 test2066 test2067 test2068 test2069 test2070 test2071 test2072 test2073 test2074 test2075 test2076 test2077 test2078 test2079 test2080 test2081 test2082 test2083 test2084 test2085 test2086 test2087 test2088 test2089 test2090 test2091 test2092 test2093 test2094 test2095 test2096 test2097 test2098 test2099 test2100 test2101 test2102 test2103 test2104 test2105 test2106 test2107 test2108 test2109 test2110 test2111 test2112 test2113 test2114 test2115 test2116 test2117 test2118 test2119 test2120 test2121 test2122 test2123 test2124 test2125 test2126 test2127 test2128 test2129 test2130 test2131 test2132 test2133 test2134 test2135 test2136 test2137 test2138 test2139 test2140 test2141 test2142 test2143 test2144 test2145 test2146 test2147 test2148 test2149 test2150 test2151 test2152 test2153 test2154 test2155 test2156 test2157 test2158 test2159 test2160 test2161 test2162 test2163 test2164 test2165 test2166 test2167 test2168 test2169 test2170 test2171 test2172 test2173 test2174 test2175 test2176 test2177 test2178 test2179 test2180 test2181 test2182 test2183 test2184 test2185 test2186 test2187 test2188 test2189 test2190 test2191 test2192 test2193 test2194 test2195 test2196 test2197 test2198 test2199 test2200 test2201 test2202 test2203 test2204 test2205 test2206 test2207 test2208 test2209 test2210 test2211 test2212 test2213 test2214 test2215 test2216 test2217 test2218 test2219 test2220 test2221 test2222 test2223 test2224 test2225 test2226 test2227 test2228 test2229 test2230 test2231 test2232 test2233 test2234 test2235 test2236 test2237 test2238 test2239 test2240 test2241 test2242 test2243 test2244 test2245 test2246 test2247 test2248 test2249 test2250 test2251 test2252 test2253 test2254 test2255 test2256 test2257 test2258 test2259 test2260 test2261 test2262 test2263 test2264 test2265 test2266 test2267 test2268 test2269 test2270 test2271 test2272 test2273 test2274 test2275 test2276 test2277 test2278 test2279 test2280 test2281 test2282 test2283 test2284 test2285 test2286 test2287 test2288 test2289 test2290 test2291 test2292 test2293 test2294 test2295 test2296 test2297 test2298 test2299 test2300 test2301 test2302 test2303 test2304 test2305 test2306 test2307 test2308 test2309 test2310 test2311 test2312 test2313 test2314 test2315 test2316 test2317 test2318 test2319 test2320 test2321 test2322 test2323 test2324 test2325 test2326 test2327 test2328 test2329 test2330 test2331 test2332 test2333 test2334 test2335 test2336 test2337 test2338 test2339 test2340 test2341 test2342 test2343 test2344 test2345 test2346 test2347 test2348 test2349 test2350 test2351 test2352 test2353 test2354 test2355 test2356 test2357 test2358 test2359 test2360 test2361 test2362 test2363 test2364 test2365 test2366 test2367 test2368 test2369 test2370 test2371 test2372 test2373 test2374 test2375 test2376 test2377 test2378 test2379 test2380 test2381 test2382 test2383 test2384 test2385 test2386 test2387 test2388 test2389 test2390 test2391 test2392 test2393 test2394 test2395 test2396 test2397 test2398 test2399 test2400 test2401 test2402 test2403 test2404 test2405 test2406 test2407 test2408 test2409 test2410 test2411 test2412 test2413 test2414 test2415 test2416 test2417 test2418 test2419 test2420 test2421 test2422 test2423 test2424 test2425 test2426 test2427 test2428 test2429 test2430 test2431 test2432 test2433 test2434 test2435 test2436 test2437 test2438 test2439 test2440 test2441 test2442 test2443 test2444 test2445 test2446 test2447 test2448 test2449 test2450 test2451 test2452 test2453 test2454 test2455 test2456 test2457 test2458 test2459 test2460 test2461 test2462 test2463 test2464 test2465 test2466 test2467 test2468 test2469 test2470 test2471 test2472 test2473 test2474 test2475 test2476 test2477 test2478 test2479 test2480 test2481 test2482 test2483 test2484 test2485 test2486 test2487 test2488 test2489 test2490 test2491 test2492 test2493 test2494 test2495 test2496 test2497 test2498 test2499 test2500 test2501 test2502 test2503 test2504 test2505 test2506 test2507 test2508 test2509 test2510 test2511 test2512 test2513 test2514 test2515 test2516 test2517 test2518 test2519 test2520 test2521 test2522 test2523 test2524 test2525 test2526 test2527 test2528 test2529 test2530 test2531 test2532 test2533 test2534 test2535 test2536 test2537 test2538 test2539 test2540 test2541 test2542 test2543 test2544 test2545 test2546 test2547 test2548 test2549 test2550 test2551 test2552 test2553 test2554 test2555 test2556 test2557 test2558 test2559 test2560 test2561 test2562 test2563 test2564 test2565 test2566 test2567 test2568 test2569 test2570 test2571 test2572 test2573 test2574 test2575 test2576 test2577 test2578 test2579 test2580 test2581 test2582 test2583 test2584 test2585 test2586 test2587 test2588 test2589 test2590 test2591 test2592 test2593 test2594 test2595 test2596 test2597 test2598 test2599 test2600 test2601 test2602 test2603 test2604 test2605 test2606 test2607 test2608 test2609 test2610 test2611 test2612 test2613 test2614 test2615 test2616 test2617 test2618 test2619 test2620 test2621 test2622 test2623 test2624 test2625 test2626 test2627 test2628 test2629 test2630 test2631 test2632 test2633 test2634 test2635 test2636 test2637 test2638 test2639 test2640 test2641 test2642 test2643 test2644 test2645 test2646 test2647 test2648 test2649 test2650 test2651 test2652 test2653 test2654 test2655 test2656 test2657 test2658 test2659 test2660 test2661 test2662 test2663 test2664 test2665 test2666 test2667 test2668 test2669 test2670 test2671 test2672 test2673 test2674 test2675 test2676 test2677 test2678 test2679 test2680 test2681 test2682 test2683 test2684 test2685 test2686 test2687 test2688 test2689 test2690 test2691 test2692 test2693 test2694 test2695 test2696 test2697 test2698 test2699 test2700 test2701 test2702 test2703 test2704 test2705 test2706 test2707 test2708 test2709 test2710 test2711 test2712 test2713 test2714 test2715 test2716 test2717 test2718 test2719 test2720 test2721 test2722 test2723 test2724 test2725 test2726 test2727 test2728 test2729 test2730 test2731 test2732 test2733 test2734 test2735 test2736 test2737 test2738 test2739 test2740 test2741 test2742 test2743 test2744 test2745 test2746 test2747 test2748 test2749 test2750 test2751 test2752 test2753 test2754 test2755 test2756 test2757 test2758 test2759 test2760 test2761 test2762 test2763 test2764 test2765 test2766 test2767 test2768 test2769 test2770 test2771 test2772 test2773 test2774 test2775 test2776 test2777 test2778 test2779 test2780 test2781 test2782 test2783 test2784 test2785 test2786 test2787 test2788 test2789 test2790 test2791 test2792 test2793 test2794 test2795 test2796 test2797 test2798 test2799 test2800 test2801 test2802 test2803 test2804 test2805 test2806 test2807 test2808 test2809 test2810 test2811 test2812 test2813 test2814 test2815 test2816 test2817 test2818 test2819 test2820 test2821 test2822 test2823 test2824 test2825 test2826 test2827 test2828 test2829 test2830 test2831 test2832 test2833 test2834 test2835 test2836 test2837 test2838 test2839 test2840 test2841 test2842 test2843 test2844 test2845 test2846 test2847 test2848 test2849 test2850 test2851 test2852 test2853 test2854 test2855 test2856 test2857 test2858 test2859 test2860 test2861 test2862 test2863 test2864 test2865 test2866 test2867 test2868 test2869 test2870 test2871 test2872 test2873 test2874 test2875 test2876 test2877 test2878 test2879 test2880 test2881 test2882 test2883 test2884 test2885 test2886 test2887 test2888 test2889 test2890 test2891 test2892 test2893 test2894 test2895 test2896 test2897 test2898 test2899 test2900 test2901 test2902 test2903 test2904 test2905 test2906 test2907 test2908 test2909 test2910 test2911 test2912 test2913 test2914 test2915 test2916 test2917 test2918 test2919 test2920 test2921 test2922 test2923 test2924 test2925 test2926 test2927 test2928 test2929 test2930 test2931 test2932 test2933 test2934 test2935 test2936 test2937 test2938 test2939 test2940 test2941 test2942 test2943 test2944 test2945 test2946 test2947 test2948 test2949 test2950 test2951 test2952 test2953 test2954 test2955 test2956 test2957 test2958 test2959 test2960 test2961 test2962 test2963 test2964 test2965 test2966 test2967 test2968 test2969 test2970 test2971 test2972 test2973 test2974 test2975 test2976 test2977 test2978 test2979 test2980 test2981 test2982 test2983 test2984 test2985 test2986 test2987 test2988 test2989 test2990 test2991 test2992 test2993 test2994 test2995 test2996 test2997 test2998 test2999 test3000 test3001 test3002 test3003 test3004 test3005 test3006 test3007 test3008 test3009 test3010 test3011 test3012 test3013 test3014 test3015 test3016 test3017 test3018 test3019 test3020 test3021 test3022 test3023 test3024 test3025 test3026 test3027 test3028 test3029 test3030 test3031 test3032 test3033 test3034 test3035 test3036 test3037 test3038 test3039 test3040 test3041 test3042 test3043 test3044 test3045 test3046 test3047 test3048 test3049 test3050 test3051 test3052 test3053 test3054 test3055 test3056 test3057 test3058 test3059 test3060 test3061 test3062 test3063 test3064 test3065 test3066 test3067 test3068 test3069 test3070 test3071 test3072 test3073 test3074 test3075 test3076 test3077 test3078 test3079 test3080 test3081 test3082 test3083 test3084 test3085 test3086 test3087 test3088 test3089 test3090 test3091 test3092 test3093 test3094 test3095 test3096 test3097 test3098 test3099 test3100 test3101 test3102 test3103 test3104 test3105 test3106 test3107 test3108 test3109 test3110 test3111 test3112 test3113 test3114 test3115 test3116 test3117 test3118 test3119 test3120 test3121 test3122 test3123 test3124 test3125 test3126 test3127 test3128 test3129 test3130 test3131 test3132 test3133 test3134 test3135 test3136 test3137 test3138 test3139 test3140 test3141 test3142 test3143 test3144 test3145 test3146 test3147 test3148 test3149 test3150 test3151 test3152 test3153 test3154 test3155 test3156 test3157 test3158 test3159 test3160 test3161 test3162 test3163 test3164 test3165 test3166 test3167 test3168 test3169 test3170 test3171 test3172 test3173 test3174 test3175 test3176 test3177 test3178 test3179 test3180 test3181 test3182 test3183 test3184 test3185 test3186 test3187 test3188 test3189 test3190 test3191 test3192 test3193 test3194 test3195 test3196 test3197 test3198 test3199 test3200 test3201 test3202 test3203 test3204 test3205 test3206 test3207 test3208 test3209 test3210 test3211 test3212 test3213 test3214 test3215 test3216 test3217 test3218 test3219 test3220 test3221 test3222 test3223 test3224 test3225 test3226 test3227 test3228 test3229 test3230 test3231 test3232 test3233 test3234 test3235 test3236 test3237 test3238 test3239 test3240 test3241 test3242 test3243 test3244 test3245 test3246 test3247 test3248 test3249 test3250 test3251 test3252 test3253 test3254 test3255 test3256 test3257 test3258 test3259 test3260 test3261 test3262 test3263 test3264 test3265 test3266 test3267 test3268 test3269 test3270 test3271 test3272 test3273 test3274 test3275 test3276 test3277 test3278 test3279 test3280 test3281 test3282 test3283 test3284 test3285 test3286 test3287 test3288 test3289 test3290 test3291 test3292 test3293 test3294 test3295 test3296 test3297 test3298 test3299 test3300 test3301 test3302 test3303 test3304 test3305 test3306 test3307 test3308 test3309 test3310 test3311 test3312 test3313 test3314 test3315 test3316 test3317 test3318 test3319 test3320 test3321 test3322 test3323 test3324 test3325 test3326 test3327 test3328 test3329 test3330 test3331 test3332 test3333 test3334 test3335 test3336 test3337 test3338 test3339 test3340 test3341 test3342 test3343 test3344 test3345 test3346 test3347 test3348 test3349 test3350 test3351 test3352 test3353 test3354 test3355 test3356 test3357 test3358 test3359 test3360 test3361 test3362 test3363 test3364 test3365 test3366 test3367 test3368 test3369 test3370 test3371 test3372 test3373 test3374 test3375 test3376 test3377 test3378 test3379 test3380 test3381 test3382 test3383 test3384 test3385 test3386 test3387 test3388 test3389 test3390 test3391 test3392 test3393 test3394 test3395 test3396 test3397 test3398 test3399 test3400 test3401 test3402 test3403 test3404 test3405 test3406 test3407 test3408 test3409 test3410 test3411 test3412 test3413 test3414 test3415 test3416 test3417 test3418 test3419 test3420 test3421 test3422 test3423 test3424 test3425 test3426 test3427 test3428 test3429 test3430 test3431 test3432 test3433 test3434 test3435 test3436 test3437 test3438 test3439 test3440 test3441 test3442 test3443 test3444 test3445 test3446 test3447 test3448 test3449 test3450 test3451 test3452 test3453 test3454 test3455 test3456 test3457 test3458 test3459 test3460 test3461 test3462 test3463 test3464 test3465 test3466 test3467 test3468 test3469 test3470 test3471 test3472 test3473 test3474 test3475 test3476 test3477 test3478 test3479 test3480 test3481 test3482 test3483 test3484 test3485 test3486 test3487 test3488 test3489 test3490 test3491 test3492 test3493 test3494 test3495 test3496 test3497 test3498 test3499 test3500 test3501 test3502 test3503 test3504 test3505 test3506 test3507 test3508 test3509 test3510 test3511 test3512 test3513 test3514 test3515 test3516 test3517 test3518 test3519 test3520 test3521 test3522 test3523 test3524 test3525 test3526 test3527 test3528 test3529 test3530 test3531 test3532 test3533 test3534 test3535 test3536 test3537 test3538 test3539 test3540 test3541 test3542 test3543 test3544 test3545 test3546 test3547 test3548 test3549 test3550 test3551 test3552 test3553 test3554 test3555 test3556 test3557 test3558 test3559 test3560 test3561 test3562 test3563 test3564 test3565 test3566 test3567 test3568 test3569 test3570 test3571 test3572 test3573 test3574 test3575 test3576 test3577 test3578 test3579 test3580 test3581 test3582 test3583 test3584 test3585 test3586 test3587 test3588 test3589 test3590 test3591 test3592 test3593 test3594 test3595 test3596 test3597 test3598 test3599 test3600 test3601 test3602 test3603 test3604 test3605 test3606 test3607 test3608 test3609 test3610 test3611 test3612 test3613 test3614 test3615 test3616 test3617 test3618 test3619 test3620 test3621 test3622 test3623 test3624 test3625 test3626 test3627 test3628 test3629 test3630 test3631 test3632 test3633 test3634 test3635 test3636 test3637 test3638 test3639 test3640 test3641 test3642 test3643 test3644 test3645 test3646 test3647 test3648 test3649 test3650 test3651 test3652 test3653 test3654 test3655 test3656 test3657 test3658 test3659 test3660 test3661 test3662 test3663 test3664 test3665 test3666 test3667 test3668 test3669 test3670 test3671 test3672 test3673 test3674 test3675 test3676 test3677 test3678 test3679 test3680 test3681 test3682 test3683 test3684 test3685 test3686 test3687 test3688 test3689 test3690 test3691 test3692 test3693 test3694 test3695 test3696 test3697 test3698 test3699 test3700 test3701 test3702 test3703 test3704 test3705 test3706 test3707 test3708 test3709 test3710 test3711 test3712 test3713 test3714 test3715 test3716 test3717 test3718 test3719 test3720 test3721 test3722 test3723 test3724 test3725 test3726 test3727 test3728 test3729 test3730 test3731 test3732 test3733 test3734 test3735 test3736 test3737 test3738 test3739 test3740 test3741 test3742 test3743 test3744 test3745 test3746 test3747 test3748 test3749 test3750 test3751 test3752 test3753 test3754 test3755 test3756 test3757 test3758 test3759 test3760 test3761 test3762 test3763 test3764 test3765 test3766 test3767 test3768 test3769 test3770 test3771 test3772 test3773 test3774 test3775 test3776 test3777 test3778 test3779 test3780 test3781 test3782 test3783 test3784 test3785 test3786 test3787 test3788 test3789 test3790 test3791 test3792 test3793 test3794 test3795 test3796 test3797 test3798 test3799 test3800 test3801 test3802 test3803 test3804 test3805 test3806 test3807 test3808 test3809 test3810 test3811 test3812 test3813 test3814 test3815 test3816 test3817 test3818 test3819 test3820 test3821 test3822 test3823 test3824 test3825 test3826 test3827 test3828 test3829 test3830 test3831 test3832 test3833 test3834 test3835 test3836 test3837 test3838 test3839 test3840 test3841 test3842 test3843 test3844 test3845 test3846 test3847 test3848 test3849 test3850 test3851 test3852 test3853 test3854 test3855 test3856 test3857 test3858 test3859 test3860 test3861 test3862 test3863 test3864 test3865 test3866 test3867 test3868 test3869 test3870 test3871 test3872 test3873 test3874 test3875 test3876 test3877 test3878 test3879 test3880 test3881 test3882 test3883 test3884 test3885 test3886 test3887 test3888 test3889 test3890 test3891 test3892 test3893 test3894 test3895 test3896 test3897 test3898 test3899 test3900 test3901 test3902 test3903 test3904 test3905 test3906 test3907 test3908 test3909 test3910 test3911 test3912 test3913 test3914 test3915 test3916 test3917 test3918 test3919 test3920 test3921 test3922 test3923 test3924 test3925 test3926 test3927 test3928 test3929 test3930 test3931 test3932 test3933 test3934 test3935 test3936 test3937 test3938 test3939 test3940 test3941 test3942 test3943 test3944 test3945 test3946 test3947 test3948 test3949 test3950 test3951 test3952 test3953 test3954 test3955 test3956 test3957 test3958 test3959 test3960 test3961 test3962 test3963 test3964 test3965 test3966 test3967 test3968 test3969 test3970 test3971 test3972 test3973 test3974 test3975 test3976 test3977 test3978 test3979 test3980 test3981 test3982 test3983 test3984 test3985 test3986 test3987 test3988 test3989 test3990 test3991 test3992 test3993 test3994 test3995 test3996 test3997 test3998 test3999 test4000 test4001 test4002 test4003 test4004 test4005 test4006 test4007 test4008 test4009 test4010 test4011 test4012 test4013 test4014 test4015 test4016 test4017 test4018 test4019 test4020 test4021 test4022 test4023 test4024 test4025 test4026 test4027 test4028 test4029 test4030 test4031 test4032 test4033 test4034 test4035 test4036 test4037 test4038 test4039 test4040 test4041 test4042 test4043 test4044 test4045 test4046 test4047 test4048 test4049 test4050 test4051 test4052 test4053 test4054 test4055 test4056 test4057 test4058 test4059 test4060 test4061 test4062 test4063 test4064 test4065 test4066 test4067 test4068 test4069 test4070 test4071 test4072 test4073 test4074 test4075 test4076 test4077 test4078 test4079 test4080 test4081 test4082 test4083 test4084 test4085 test4086 test4087 test4088 test4089 test4090 test4091 test4092 test4093 test4094 test4095 test4096 test4097 test4098 test4099 test4100 test4101 test4102 test4103 test4104 test4105 test4106 test4107 test4108 test4109 test4110 test4111 test4112 test4113 test4114 test4115 test4116 test4117 test4118 test4119 test4120 test4121 test4122 test4123 test4124 test4125 test4126 test4127 test4128 test4129 test4130 test4131 test4132 test4133 test4134 test4135 test4136 test4137 test4138 test4139 test4140 test4141 test4142 test4143 test4144 test4145 test4146 test4147 test4148 test4149 test4150 test4151 test4152 test4153 test4154 test4155 test4156 test4157 test4158 test4159 test4160 test4161 test4162 test4163 test4164 test4165 test4166 test4167 test4168 test4169 test4170 test4171 test4172 test4173 test4174 test4175 test4176 test4177 test4178 test4179 test4180 test4181 test4182 test4183 test4184 test4185 test4186 test4187 test4188 test4189 test4190 test4191 test4192 test4193 test4194 test4195 test4196 test4197 test4198 test4199 test4200 test4201 test4202 test4203 test4204 test4205 test4206 test4207 test4208 test4209 test4210 test4211 test4212 test4213 test4214 test4215 test4216 test4217 test4218 test4219 test4220 test4221 test4222 test4223 test4224 test4225 test4226 test4227 test4228 test4229 test4230 test4231 test4232 test4233 test4234 test4235 test4236 test4237 test4238 test4239 test4240 test4241 test4242 test4243 test4244 test4245 test4246 test4247 test4248 test4249 test4250 test4251 test4252 test4253 test4254 test4255 test4256 test4257 test4258 test4259 test4260 test4261 test4262 test4263 test4264 test4265 test4266 test4267 test4268 test4269 test4270 test4271 test4272 test4273 test4274 test4275 test4276 test4277 test4278 test4279 test4280 test4281 test4282 test4283 test4284 test4285 test4286 test4287 test4288 test4289 test4290 test4291 test4292 test4293 test4294 test4295 test4296 test4297 test4298 test4299 test4300 test4301 test4302 test4303 test4304 test4305 test4306 test4307 test4308 test4309 test4310 test4311 test4312 test4313 test4314 test4315 test4316 test4317 test4318 test4319 test4320 test4321 test4322 test4323 test4324 test4325 test4326 test4327 test4328 test4329 test4330 test4331 test4332 test4333 test4334 test4335 test4336 test4337 test4338 test4339 test4340 test4341 test4342 test4343 test4344 test4345 test4346 test4347 test4348 test4349 test4350 test4351 test4352 test4353 test4354 test4355 test4356 test4357 test4358 test4359 test4360 test4361 test4362 test4363 test4364 test4365 test4366 test4367 test4368 test4369 test4370 test4371 test4372 test4373 test4374 test4375 test4376 test4377 test4378 test4379 test4380 test4381 test4382 test4383 test4384 test4385 test4386 test4387 test4388 test4389 test4390 test4391 test4392 test4393 test4394 test4395 test4396 test4397 test4398 test4399 test4400 test4401 test4402 test4403 test4404 test4405 test4406 test4407 test4408 test4409 test4410 test4411 test4412 test4413 test4414 test4415 test4416 test4417 test4418 test4419 test4420 test4421 test4422 test4423 test4424 test4425 test4426 test4427 test4428 test4429 test4430 test4431 test4432 test4433 test4434 test4435 test4436 test4437 test4438 test4439 test4440 test4441 test4442 test4443 test4444 test4445 test4446 test4447 test4448 test4449 test4450 test4451 test4452 test4453 test4454 test4455 test4456 test4457 test4458 test4459 test4460 test4461 test4462 test4463 test4464 test4465 test4466 test4467 test4468 test4469 test4470 test4471 test4472 test4473 test4474 test4475 test4476 test4477 test4478 test4479 test4480 test4481 test4482 test4483 test4484 test4485 test4486 test4487 test4488 test4489 test4490 test4491 test4492 test4493 test4494 test4495 test4496 test4497 test4498 test4499 test4500 test4501 test4502 test4503 test4504 test4505 test4506 test4507 test4508 test4509 test4510 test4511 test4512 test4513 test4514 test4515 test4516 test4517 test4518 test4519 test4520 test4521 test4522 test4523 test4524 test4525 test4526 test4527 test4528 test4529 test4530 test4531 test4532 test4533 test4534 test4535 test4536 test4537 test4538 test4539 test4540 test4541 test4542 test4543 test4544 test4545 test4546 test4547 test4548 test4549 test4550 test4551 test4552 test4553 test4554 test4555 test4556 test4557 test4558 test4559 test4560 test4561 test4562 test4563 test4564 test4565 test4566 test4567 test4568 test4569 test4570 test4571 test4572 test4573 test4574 test4575 test4576 test4577 test4578 test4579 test4580 test4581 test4582 test4583 test4584 test4585 test4586 test4587 test4588 test4589 test4590 test4591 test4592 test4593 test4594 test4595 test4596 test4597 test4598 test4599 test4600 test4601 test4602 test4603 test4604 test4605 test4606 test4607 test4608 test4609 test4610 test4611 test4612 test4613 test4614 test4615 test4616 test4617 test4618 test4619 test4620 test4621 test4622 test4623 test4624 test4625 test4626 test4627 test4628 test4629 test4630 test4631 test4632 test4633 test4634 test4635 test4636 test4637 test4638 test4639 test4640 test4641 test4642 test4643 test4644 test4645 test4646 test4647 test4648 test4649 test4650 test4651 test4652 test4653 test4654 test4655 test4656 test4657 test4658 test4659 test4660 test4661 test4662 test4663 test4664 test4665 test4666 test4667 test4668 test4669 test4670 test4671 test4672 test4673 test4674 test4675 test4676 test4677 test4678 test4679 test4680 test4681 test4682 test4683 test4684 test4685 test4686 test4687 test4688 test4689 test4690 test4691 test4692 test4693 test4694 test4695 test4696 test4697 test4698 test4699 test4700 test4701 test4702 test4703 test4704 test4705 test4706 test4707 test4708 test4709 test4710 test4711 test4712 test4713 test4714 test4715 test4716 test4717 test4718 test4719 test4720 test4721 test4722 test4723 test4724 test4725 test4726 test4727 test4728 test4729 test4730 test4731 test4732 test4733 test4734 test4735 test4736 test4737 test4738 test4739 test4740 test4741 test4742 test4743 test4744 test4745 test4746 test4747 test4748 test4749 test4750 test4751 test4752 test4753 test4754 test4755 test4756 test4757 test4758 test4759 test4760 test4761 test4762 test4763 test4764 test4765 test4766 test4767 test4768 test4769 test4770 test4771 test4772 test4773 test4774 test4775 test4776 test4777 test4778 test4779 test4780 test4781 test4782 test4783 test4784 test4785 test4786 test4787 test4788 test4789 test4790 test4791 test4792 test4793 test4794 test4795 test4796 test4797 test4798 test4799 test4800 test4801 test4802 test4803 test4804 test4805 test4806 test4807 test4808 test4809 test4810 test4811 test4812 test4813 test4814 test4815 test4816 test4817 test4818 test4819 test4820 test4821 test4822 test4823 test4824 test4825 test4826 test4827 test4828 test4829 test4830 test4831 test4832 test4833 test4834 test4835 test4836 test4837 test4838 test4839 test4840 test4841 test4842 test4843 test4844 test4845 test4846 test4847 test4848 test4849 test4850 test4851 test4852 test4853 test4854 test4855 test4856 test4857 test4858 test4859 test4860 test4861 test4862 test4863 test4864 test4865 test4866 test4867 test4868 test4869 test4870 test4871 test4872 test4873 test4874 test4875 test4876 test4877 test4878 test4879 test4880 test4881 test4882 test4883 test4884 test4885 test4886 test4887 test4888 test4889 test4890 test4891 test4892 test4893 test4894 test4895 test4896 test4897 test4898 test4899 test4900 test4901 test4902 test4903 test4904 test4905 test4906 test4907 test4908 test4909 test4910 test4911 test4912 test4913 test4914 test4915 test4916 test4917 test4918 test4919 test4920 test4921 test4922 test4923 test4924 test4925 test4926 test4927 test4928 test4929 test4930 test4931 test4932 test4933 test4934 test4935 test4936 test4937 test4938 test4939 test4940 test4941 test4942 test4943 test4944 test4945 test4946 test4947 test4948 test4949 test4950 test4951 test4952 test4953 test4954 test4955 test4956 test4957 test4958 test4959 test4960 test4961 test4962 test4963 test4964 test4965 test4966 test4967 test4968 test4969 test4970 test4971 test4972 test4973 test4974 test4975 test4976 test4977 test4978 test4979 test4980 test4981 test4982 test4983 test4984 test4985 test4986 test4987 test4988 test4989 test4990 test4991 test4992 test4993 test4994 test4995 test4996 test4997 test4998 test4999 test5000 test5001 test5002 test5003 test5004 test5005 test5006 test5007 test5008 test5009 test5010 test5011 test5012 test5013 test5014 test5015 test5016 test5017 test5018 test5019 test5020 test5021 test5022 test5023 test5024 test5025 test5026 test5027 test5028 test5029 test5030 test5031 test5032 test5033 test5034 test5035 test5036 test5037 test5038 test5039 test5040 test5041 test5042 test5043 test5044 test5045 test5046 test5047 test5048 test5049 test5050 test5051 test5052 test5053 test5054 test5055 test5056 test5057 test5058 test5059 test5060 test5061 test5062 test5063 test5064 test5065 test5066 test5067 test5068 test5069 test5070 test5071 test5072 test5073 test5074 test5075 test5076 test5077 test5078 test5079 test5080 test5081 test5082 test5083 test5084 test5085 test5086 test5087 test5088 test5089 test5090 test5091 test5092 test5093 test5094 test5095 test5096 test5097 test5098 test5099 test5100 test5101 test5102 test5103 test5104 test5105 test5106 test5107 test5108 test5109 test5110 test5111 test5112 test5113 test5114 test5115 test5116 test5117 test5118 test5119 test5120 test5121 test5122 test5123 test5124 test5125 test5126 test5127 test5128 test5129 test5130 test5131 test5132 test5133 test5134 test5135 test5136 test5137 test5138 test5139 test5140 test5141 test5142 test5143 test5144 test5145 test5146 test5147 test5148 test5149 test5150 test5151 test5152 test5153 test5154 test5155 test5156 test5157 test5158 test5159 test5160 test5161 test5162 test5163 test5164 test5165 test5166 test5167 test5168 test5169 test5170 test5171 test5172 test5173 test5174 test5175 test5176 test5177 test5178 test5179 test5180 test5181 test5182 test5183 test5184 test5185 test5186 test5187 test5188 test5189 test5190 test5191 test5192 test5193 test5194 test5195 test5196 test5197 test5198 test5199 test5200 test5201 test5202 test5203 test5204 test5205 test5206 test5207 test5208 test5209 test5210 test5211 test5212 test5213 test5214 test5215 test5216 test5217 test5218 test5219 test5220 test5221 test5222 test5223 test5224 test5225 test5226 test5227 test5228 test5229 test5230 test5231 test5232 test5233 test5234 test5235 test5236 test5237 test5238 test5239 test5240 test5241 test5242 test5243 test5244 test5245 test5246 test5247 test5248 test5249 test5250 test5251 test5252 test5253 test5254 test5255 test5256 test5257 test5258 test5259 test5260 test5261 test5262 test5263 test5264 test5265 test5266 test5267 test5268 test5269 test5270 test5271 test5272 test5273 test5274 test5275 test5276 test5277 test5278 test5279 test5280 test5281 test5282 test5283 test5284 test5285 test5286 test5287 test5288 test5289 test5290 test5291 test5292 test5293 test5294 test5295 test5296 test5297 test5298 test5299 test5300 test5301 test5302 test5303 test5304 test5305 test5306 test5307 test5308 test5309 test5310 test5311 test5312 test5313 test5314 test5315 test5316 test5317 test5318 test5319 test5320 test5321 test5322 test5323 test5324 test5325 test5326 test5327 test5328 test5329 test5330 test5331 test5332 test5333 test5334 test5335 test5336 test5337 test5338 test5339 test5340 test5341 test5342 test5343 test5344 test5345 test5346 test5347 test5348 test5349 test5350 test5351 test5352 test5353 test5354 test5355 test5356 test5357 test5358 test5359 test5360 test5361 test5362 test5363 test5364 test5365 test5366 test5367 test5368 test5369 test5370 test5371 test5372 test5373 test5374 test5375 test5376 test5377 test5378 test5379 test5380 test5381 test5382 test5383 test5384 test5385 test5386 test5387 test5388 test5389 test5390 test5391 test5392 test5393 test5394 test5395 test5396 test5397 test5398 test5399 test5400 test5401 test5402 test5403 test5404 test5405 test5406 test5407 test5408 test5409 test5410 test5411 test5412 test5413 test5414 test5415 test5416 test5417 test5418 test5419 test5420 test5421 test5422 test5423 test5424 test5425 test5426 test5427 test5428 test5429 test5430 test5431 test5432 test5433 test5434 test5435 test5436 test5437 test5438 test5439 test5440 test5441 test5442 test5443 test5444 test5445 test5446 test5447 test5448 test5449 test5450 test5451 test5452 test5453 test5454 test5455 test5456 test5457 test5458 test5459 test5460 test5461 test5462 test5463 test5464 test5465 test5466 test5467 test5468 test5469 test5470 test5471 test5472 test5473 test5474 test5475 test5476 test5477 test5478 test5479 test5480 test5481 test5482 test5483 test5484 test5485 test5486 test5487 test5488 test5489 test5490 test5491 test5492 test5493 test5494 test5495 test5496 test5497 test5498 test5499 test5500 test5501 test5502 test5503 test5504 test5505 test5506 test5507 test5508 test5509 test5510 test5511 test5512 test5513 test5514 test5515 test5516 test5517 test5518 test5519 test5520 test5521 test5522 test5523 test5524 test5525 test5526 test5527 test5528 test5529 test5530 test5531 test5532 test5533 test5534 test5535 test5536 test5537 test5538 test5539 test5540 test5541 test5542 test5543 test5544 test5545 test5546 test5547 test5548 test5549 test5550 test5551 test5552 test5553 test5554 test5555 test5556 test5557 test5558 test5559 test5560 test5561 test5562 test5563 test5564 test5565 test5566 test5567 test5568 test5569 test5570 test5571 test5572 test5573 test5574 test5575 test5576 test5577 test5578 test5579 test5580 test5581 test5582 test5583 test5584 test5585 test5586 test5587 test5588 test5589 test5590 test5591 test5592 test5593 test5594 test5595 test5596 test5597 test5598 test5599 test5600 test5601 test5602 test5603 test5604 test5605 test5606 test5607 test5608 test5609 test5610 test5611 test5612 test5613 test5614 test5615 test5616 test5617 test5618 test5619 test5620 test5621 test5622 test5623 test5624 test5625 test5626 test5627 test5628 test5629 test5630 test5631 test5632 test5633 test5634 test5635 test5636 test5637 test5638 test5639 test5640 test5641 test5642 test5643 test5644 test5645 test5646 test5647 test5648 test5649 test5650 test5651 test5652 test5653 test5654 test5655 test5656 test5657 test5658 test5659 test5660 test5661 test5662 test5663 test5664 test5665 test5666 test5667 test5668 test5669 test5670 test5671 test5672 test5673 test5674 test5675 test5676 test5677 test5678 test5679 test5680 test5681 test5682 test5683 test5684 test5685 test5686 test5687 test5688 test5689 test5690 test5691 test5692 test5693 test5694 test5695 test5696 test5697 test5698 test5699 test5700 test5701 test5702 test5703 test5704 test5705 test5706 test5707 test5708 test5709 test5710 test5711 test5712 test5713 test5714 test5715 test5716 test5717 test5718 test5719 test5720 test5721 test5722 test5723 test5724 test5725 test5726 test5727 test5728 test5729 test5730 test5731 test5732 test5733 test5734 test5735 test5736 test5737 test5738 test5739 test5740 test5741 test5742 test5743 test5744 test5745 test5746 test5747 test5748 test5749 test5750 test5751 test5752 test5753 test5754 test5755 test5756 test5757 test5758 test5759 test5760 test5761 test5762 test5763 test5764 test5765 test5766 test5767 test5768 test5769 test5770 test5771 test5772 test5773 test5774 test5775 test5776 test5777 test5778 test5779 test5780 test5781 test5782 test5783 test5784 test5785 test5786 test5787 test5788 test5789 test5790 test5791 test5792 test5793 test5794 test5795 test5796 test5797 test5798 test5799 test5800 test5801 test5802 test5803 test5804 test5805 test5806 test5807 test5808 test5809 test5810 test5811 test5812 test5813 test5814 test5815 test5816 test5817 test5818 test5819 test5820 test5821 test5822 test5823 test5824 test5825 test5826 test5827 test5828 test5829 test5830 test5831 test5832 test5833 test5834 test5835 test5836 test5837 test5838 test5839 test5840 test5841 test5842 test5843 test5844 test5845 test5846 test5847 test5848 test5849 test5850 test5851 test5852 test5853 test5854 test5855 test5856 test5857 test5858 test5859 test5860 test5861 test5862 test5863 test5864 test5865 test5866 test5867 test5868 test5869 test5870 test5871 test5872 test5873 test5874 test5875 test5876 test5877 test5878 test5879 test5880 test5881 test5882 test5883 test5884 test5885 test5886 test5887 test5888 test5889 test5890 test5891 test5892 test5893 test5894 test5895 test5896 test5897 test5898 test5899 test5900 test5901 test5902 test5903 test5904 test5905 test5906 test5907 test5908 test5909 test5910 test5911 test5912 test5913 test5914 test5915 test5916 test5917 test5918 test5919 test5920 test5921 test5922 test5923 test5924 test5925 test5926 test5927 test5928 test5929 test5930 test5931 test5932 test5933 test5934 test5935 test5936 test5937 test5938 test5939 test5940 test5941 test5942 test5943 test5944 test5945 test5946 test5947 test5948 test5949 test5950 test5951 test5952 test5953 test5954 test5955 test5956 test5957 test5958 test5959 test5960 test5961 test5962 test5963 test5964 test5965 test5966 test5967 test5968 test5969 test5970 test5971 test5972 test5973 test5974 test5975 test5976 test5977 test5978 test5979 test5980 test5981 test5982 test5983 test5984 test5985 test5986 test5987 test5988 test5989 test5990 test5991 test5992 test5993 test5994 test5995 test5996 test5997 test5998 test5999 test6000 test6001 test6002 test6003 test6004 test6005 test6006 test6007 test6008 test6009 test6010 test6011 test6012 test6013 test6014 test6015 test6016 test6017 test6018 test6019 test6020 test6021 test6022 test6023 test6024 test6025 test6026 test6027 test6028 test6029 test6030 test6031 test6032 test6033 test6034 test6035 test6036 test6037 test6038 test6039 test6040 test6041 test6042 test6043 test6044 test6045 test6046 test6047 test6048 test6049 test6050 test6051 test6052 test6053 test6054 test6055 test6056 test6057 test6058 test6059 test6060 test6061 test6062 test6063 test6064 test6065 test6066 test6067 test6068 test6069 test6070 test6071 test6072 test6073 test6074 test6075 test6076 test6077 test6078 test6079 test6080 test6081 test6082 test6083 test6084 test6085 test6086 test6087 test6088 test6089 test6090 test6091 test6092 test6093 test6094 test6095 test6096 test6097 test6098 test6099 test6100 test6101 test6102 test6103 test6104 test6105 test6106 test6107 test6108 test6109 test6110 test6111 test6112 test6113 test6114 test6115 test6116 test6117 test6118 test6119 test6120 test6121 test6122 test6123 test6124 test6125 test6126 test6127 test6128 test6129 test6130 test6131 test6132 test6133 test6134 test6135 test6136 test6137 test6138 test6139 test6140 test6141 test6142 test6143 test6144 test6145 test6146 test6147 test6148 test6149 test6150 test6151 test6152 test6153 test6154 test6155 test6156 test6157 test6158 test6159 test6160 test6161 test6162 test6163 test6164 test6165 test6166 test6167 test6168 test6169 test6170 test6171 test6172 test6173 test6174 test6175 test6176 test6177 test6178 test6179 test6180 test6181 test6182 test6183 test6184 test6185 test6186 test6187 test6188 test6189 test6190 test6191 test6192 test6193 test6194 test6195 test6196 test6197 test6198 test6199 test6200 test6201 test6202 test6203 test6204 test6205 test6206 test6207 test6208 test6209 test6210 test6211 test6212 test6213 test6214 test6215 test6216 test6217 test6218 test6219 test6220 test6221 test6222 test6223 test6224 test6225 test6226 test6227 test6228 test6229 test6230 test6231 test6232 test6233 test6234 test6235 test6236 test6237 test6238 test6239 test6240 test6241 test6242 test6243 test6244 test6245 test6246 test6247 test6248 test6249 test6250 test6251 test6252 test6253 test6254 test6255 test6256 test6257 test6258 test6259 test6260 test6261 test6262 test6263 test6264 test6265 test6266 test6267 test6268 test6269 test6270 test6271 test6272 test6273 test6274 test6275 test6276 test6277 test6278 test6279 test6280 test6281 test6282 test6283 test6284 test6285 test6286 test6287 test6288 test6289 test6290 test6291 test6292 test6293 test6294 test6295 test6296 test6297 test6298 test6299 test6300 test6301 test6302 test6303 test6304 test6305 test6306 test6307 test6308 test6309 test6310 test6311 test6312 test6313 test6314 test6315 test6316 test6317 test6318 test6319 test6320 test6321 test6322 test6323 test6324 test6325 test6326 test6327 test6328 test6329 test6330 test6331 test6332 test6333 test6334 test6335 test6336 test6337 test6338 test6339 test6340 test6341 test6342 test6343 test6344 test6345 test6346 test6347 test6348 test6349 test6350 test6351 test6352 test6353 test6354 test6355 test6356 test6357 test6358 test6359 test6360 test6361 test6362 test6363 test6364 test6365 test6366 test6367 test6368 test6369 test6370 test6371 test6372 test6373 test6374 test6375 test6376 test6377 test6378 test6379 test6380 test6381 test6382 test6383 test6384 test6385 test6386 test6387 test6388 test6389 test6390 test6391 test6392 test6393 test6394 test6395 test6396 test6397 test6398 test6399 test6400 test6401 test6402 test6403 test6404 test6405 test6406 test6407 test6408 test6409 test6410 test6411 test6412 test6413 test6414 test6415 test6416 test6417 test6418 test6419 test6420 test6421 test6422 test6423 test6424 test6425 test6426 test6427 test6428 test6429 test6430 test6431 test6432 test6433 test6434 test6435 test6436 test6437 test6438 test6439 test6440 test6441 test6442 test6443 test6444 test6445 test6446 test6447 test6448 test6449 test6450 test6451 test6452 test6453 test6454 test6455 test6456 test6457 test6458 test6459 test6460 test6461 test6462 test6463 test6464 test6465 test6466 test6467 test6468 test6469 test6470 test6471 test6472 test6473 test6474 test6475 test6476 test6477 test6478 test6479 test6480 test6481 test6482 test6483 test6484 test6485 test6486 test6487 test6488 test6489 test6490 test6491 test6492 test6493 test6494 test6495 test6496 test6497 test6498 test6499 test6500 test6501 test6502 test6503 test6504 test6505 test6506 test6507 test6508 test6509 test6510 test6511 test6512 test6513 test6514 test6515 test6516 test6517 test6518 test6519 test6520 test6521 test6522 test6523 test6524 test6525 test6526 test6527 test6528 test6529 test6530 test6531 test6532 test6533 test6534 test6535 test6536 test6537 test6538 test6539 test6540 test6541 test6542 test6543 test6544 test6545 test6546 test6547 test6548 test6549 test6550 test6551 test6552 test6553 test6554 test6555 test6556 test6557 test6558 test6559 test6560 test6561 test6562 test6563 test6564 test6565 test6566 test6567 test6568 test6569 test6570 test6571 test6572 test6573 test6574 test6575 test6576 test6577 test6578 test6579 test6580 test6581 test6582 test6583 test6584 test6585 test6586 test6587 test6588 test6589 test6590 test6591 test6592 test6593 test6594 test6595 test6596 test6597 test6598 test6599 test6600 test6601 test6602 test6603 test6604 test6605 test6606 test6607 test6608 test6609 test6610 test6611 test6612 test6613 test6614 test6615 test6616 test6617 test6618 test6619 test6620 test6621 test6622 test6623 test6624 test6625 test6626 test6627 test6628 test6629 test6630 test6631 test6632 test6633 test6634 test6635 test6636 test6637 test6638 test6639 test6640 test6641 test6642 test6643 test6644 test6645 test6646 test6647 test6648 test6649 test6650 test6651 test6652 test6653 test6654 test6655 test6656 test6657 test6658 test6659 test6660 test6661 test6662 test6663 test6664 test6665 test6666 test6667 test6668 test6669 test6670 test6671 test6672 test6673 test6674 test6675 test6676 test6677 test6678 test6679 test6680 test6681 test6682 test6683 test6684 test6685 test6686 test6687 test6688 test6689 test6690 test6691 test6692 test6693 test6694 test6695 test6696 test6697 test6698 test6699 test6700 test6701 test6702 test6703 test6704 test6705 test6706 test6707 test6708 test6709 test6710 test6711 test6712 test6713 test6714 test6715 test6716 test6717 test6718 test6719 test6720 test6721 test6722 test6723 test6724 test6725 test6726 test6727 test6728 test6729 test6730 test6731 test6732 test6733 test6734 test6735 test6736 test6737 test6738 test6739 test6740 test6741 test6742 test6743 test6744 test6745 test6746 test6747 test6748 test6749 test6750 test6751 test6752 test6753 test6754 test6755 test6756 test6757 test6758 test6759 test6760 test6761 test6762 test6763 test6764 test6765 test6766 test6767 test6768 test6769 test6770 test6771 test6772 test6773 test6774 test6775 test6776 test6777 test6778 test6779 test6780 test6781 test6782 test6783 test6784 test6785 test6786 test6787 test6788 test6789 test6790 test6791 test6792 test6793 test6794 test6795 test6796 test6797 test6798 test6799 test6800 test6801 test6802 test6803 test6804 test6805 test6806 test6807 test6808 test6809 test6810 test6811 test6812 test6813 test6814 test6815 test6816 test6817 test6818 test6819 test6820 test6821 test6822 test6823 test6824 test6825 test6826 test6827 test6828 test6829 test6830 test6831 test6832 test6833 test6834 test6835 test6836 test6837 test6838 test6839 test6840 test6841 test6842 test6843 test6844 test6845 test6846 test6847 test6848 test6849 test6850 test6851 test6852 test6853 test6854 test6855 test6856 test6857 test6858 test6859 test6860 test6861 test6862 test6863 test6864 test6865 test6866 test6867 test6868 test6869 test6870 test6871 test6872 test6873 test6874 test6875 test6876 test6877 test6878 test6879 test6880 test6881 test6882 test6883 test6884 test6885 test6886 test6887 test6888 test6889 test6890 test6891 test6892 test6893 test6894 test6895 test6896 test6897 test6898 test6899 test6900 test6901 test6902 test6903 test6904 test6905 test6906 test6907 test6908 test6909 test6910 test6911 test6912 test6913 test6914 test6915 test6916 test6917 test6918 test6919 test6920 test6921 test6922 test6923 test6924 test6925 test6926 test6927 test6928 test6929 test6930 test6931 test6932 test6933 test6934 test6935 test6936 test6937 test6938 test6939 test6940 test6941 test6942 test6943 test6944 test6945 test6946 test6947 test6948 test6949 test6950 test6951 test6952 test6953 test6954 test6955 test6956 test6957 test6958 test6959 test6960 test6961 test6962 test6963 test6964 test6965 test6966 test6967 test6968 test6969 test6970 test6971 test6972 test6973 test6974 test6975 test6976 test6977 test6978 test6979 test6980 test6981 test6982 test6983 test6984 test6985 test6986 test6987 test6988 test6989 test6990 test6991 test6992 test6993 test6994 test6995 test6996 test6997 test6998 test6999 test7000 test7001 test7002 test7003 test7004 test7005 test7006 test7007 test7008 test7009 test7010 test7011 test7012 test7013 test7014 test7015 test7016 test7017 test7018 test7019 test7020 test7021 test7022 test7023 test7024 test7025 test7026 test7027 test7028 test7029 test7030 test7031 test7032 test7033 test7034 test7035 test7036 test7037 test7038 test7039 test7040 test7041 test7042 test7043 test7044 test7045 test7046 test7047 test7048 test7049 test7050 test7051 test7052 test7053 test7054 test7055 test7056 test7057 test7058 test7059 test7060 test7061 test7062 test7063 test7064 test7065 test7066 test7067 test7068 test7069 test7070 test7071 test7072 test7073 test7074 test7075 test7076 test7077 test7078 test7079 test7080 test7081 test7082 test7083 test7084 test7085 test7086 test7087 test7088 test7089 test7090 test7091 test7092 test7093 test7094 test7095 test7096 test7097 test7098 test7099 test7100 test7101 test7102 test7103 test7104 test7105 test7106 test7107 test7108 test7109 test7110 test7111 test7112 test7113 test7114 test7115 test7116 test7117 test7118 test7119 test7120 test7121 test7122 test7123 test7124 test7125 test7126 test7127 test7128 test7129 test7130 test7131 test7132 test7133 test7134 test7135 test7136 test7137 test7138 test7139 test7140 test7141 test7142 test7143 test7144 test7145 test7146 test7147 test7148 test7149 test7150 test7151 test7152 test7153 test7154 test7155 test7156 test7157 test7158 test7159 test7160 test7161 test7162 test7163 test7164 test7165 test7166 test7167 test7168 test7169 test7170 test7171 test7172 test7173 test7174 test7175 test7176 test7177 test7178 test7179 test7180 test7181 test7182 test7183 test7184 test7185 test7186 test7187 test7188 test7189 test7190 test7191 test7192 test7193 test7194 test7195 test7196 test7197 test7198 test7199 test7200 test7201 test7202 test7203 test7204 test7205 test7206 test7207 test7208 test7209 test7210 test7211 test7212 test7213 test7214 test7215 test7216 test7217 test7218 test7219 test7220 test7221 test7222 test7223 test7224 test7225 test7226 test7227 test7228 test7229 test7230 test7231 test7232 test7233 test7234 test7235 test7236 test7237 test7238 test7239 test7240 test7241 test7242 test7243 test7244 test7245 test7246 test7247 test7248 test7249 test7250 test7251 test7252 test7253 test7254 test7255 test7256 test7257 test7258 test7259 test7260 test7261 test7262 test7263 test7264 test7265 test7266 test7267 test7268 test7269 test7270 test7271 test7272 test7273 test7274 test7275 test7276 test7277 test7278 test7279 test7280 test7281 test7282 test7283 test7284 test7285 test7286 test7287 test7288 test7289 test7290 test7291 test7292 test7293 test7294 test7295 test7296 test7297 test7298 test7299 test7300 test7301 test7302 test7303 test7304 test7305 test7306 test7307 test7308 test7309 test7310 test7311 test7312 test7313 test7314 test7315 test7316 test7317 test7318 test7319 test7320 test7321 test7322 test7323 test7324 test7325 test7326 test7327 test7328 test7329 test7330 test7331 test7332 test7333 test7334 test7335 test7336 test7337 test7338 test7339 test7340 test7341 test7342 test7343 test7344 test7345 test7346 test7347 test7348 test7349 test7350 test7351 test7352 test7353 test7354 test7355 test7356 test7357 test7358 test7359 test7360 test7361 test7362 test7363 test7364 test7365 test7366 test7367 test7368 test7369 test7370 test7371 test7372 test7373 test7374 test7375 test7376 test7377 test7378 test7379 test7380 test7381 test7382 test7383 test7384 test7385 test7386 test7387 test7388 test7389 test7390 test7391 test7392 test7393 test7394 test7395 test7396 test7397 test7398 test7399 test7400 test7401 test7402 test7403 test7404 test7405 test7406 test7407 test7408 test7409 test7410 test7411 test7412 test7413 test7414 test7415 test7416 test7417 test7418 test7419 test7420 test7421 test7422 test7423 test7424 test7425 test7426 test7427 test7428 test7429 test7430 test7431 test7432 test7433 test7434 test7435 test7436 test7437 test7438 test7439 test7440 test7441 test7442 test7443 test7444 test7445 test7446 test7447 test7448 test7449 test7450 test7451 test7452 test7453 test7454 test7455 test7456 test7457 test7458 test7459 test7460 test7461 test7462 test7463 test7464 test7465 test7466 test7467 test7468 test7469 test7470 test7471 test7472 test7473 test7474 test7475 test7476 test7477 test7478 test7479 test7480 test7481 test7482 test7483 test7484 test7485 test7486 test7487 test7488 test7489 test7490 test7491 test7492 test7493 test7494 test7495 test7496 test7497 test7498 test7499 test7500 test7501 test7502 test7503 test7504 test7505 test7506 test7507 test7508 test7509 test7510 test7511 test7512 test7513 test7514 test7515 test7516 test7517 test7518 test7519 test7520 test7521 test7522 test7523 test7524 test7525 test7526 test7527 test7528 test7529 test7530 test7531 test7532 test7533 test7534 test7535 test7536 test7537 test7538 test7539 test7540 test7541 test7542 test7543 test7544 test7545 test7546 test7547 test7548 test7549 test7550 test7551 test7552 test7553 test7554 test7555 test7556 test7557 test7558 test7559 test7560 test7561 test7562 test7563 test7564 test7565 test7566 test7567 test7568 test7569 test7570 test7571 test7572 test7573 test7574 test7575 test7576 test7577 test7578 test7579 test7580 test7581 test7582 test7583 test7584 test7585 test7586 test7587 test7588 test7589 test7590 test7591 test7592 test7593 test7594 test7595 test7596 test7597 test7598 test7599 test7600 test7601 test7602 test7603 test7604 test7605 test7606 test7607 test7608 test7609 test7610 test7611 test7612 test7613 test7614 test7615 test7616 test7617 test7618 test7619 test7620 test7621 test7622 test7623 test7624 test7625 test7626 test7627 test7628 test7629 test7630 test7631 test7632 test7633 test7634 test7635 test7636 test7637 test7638 test7639 test7640 test7641 test7642 test7643 test7644 test7645 test7646 test7647 test7648 test7649 test7650 test7651 test7652 test7653 test7654 test7655 test7656 test7657 test7658 test7659 test7660 test7661 test7662 test7663 test7664 test7665 test7666 test7667 test7668 test7669 test7670 test7671 test7672 test7673 test7674 test7675 test7676 test7677 test7678 test7679 test7680 test7681 test7682 test7683 test7684 test7685 test7686 test7687 test7688 test7689 test7690 test7691 test7692 test7693 test7694 test7695 test7696 test7697 test7698 test7699 test7700 test7701 test7702 test7703 test7704 test7705 test7706 test7707 test7708 test7709 test7710 test7711 test7712 test7713 test7714 test7715 test7716 test7717 test7718 test7719 test7720 test7721 test7722 test7723 test7724 test7725 test7726 test7727 test7728 test7729 test7730 test7731 test7732 test7733 test7734 test7735 test7736 test7737 test7738 test7739 test7740 test7741 test7742 test7743 test7744 test7745 test7746 test7747 test7748 test7749 test7750 test7751 test7752 test7753 test7754 test7755 test7756 test7757 test7758 test7759 test7760 test7761 test7762 test7763 test7764 test7765 test7766 test7767 test7768 test7769 test7770 test7771 test7772 test7773 test7774 test7775 test7776 test7777 test7778 test7779 test7780 test7781 test7782 test7783 test7784 test7785 test7786 test7787 test7788 test7789 test7790 test7791 test7792 test7793 test7794 test7795 test7796 test7797 test7798 test7799 test7800 test7801 test7802 test7803 test7804 test7805 test7806 test7807 test7808 test7809 test7810 test7811 test7812 test7813 test7814 test7815 test7816 test7817 test7818 test7819 test7820 test7821 test7822 test7823 test7824 test7825 test7826 test7827 test7828 test7829 test7830 test7831 test7832 test7833 test7834 test7835 test7836 test7837 test7838 test7839 test7840 test7841 test7842 test7843 test7844 test7845 test7846 test7847 test7848 test7849 test7850 test7851 test7852 test7853 test7854 test7855 test7856 test7857 test7858 test7859 test7860 test7861 test7862 test7863 test7864 test7865 test7866 test7867 test7868 test7869 test7870 test7871 test7872 test7873 test7874 test7875 test7876 test7877 test7878 test7879 test7880 test7881 test7882 test7883 test7884 test7885 test7886 test7887 test7888 test7889 test7890 test7891 test7892 test7893 test7894 test7895 test7896 test7897 test7898 test7899 test7900 test7901 test7902 test7903 test7904 test7905 test7906 test7907 test7908 test7909 test7910 test7911 test7912 test7913 test7914 test7915 test7916 test7917 test7918 test7919 test7920 test7921 test7922 test7923 test7924 test7925 test7926 test7927 test7928 test7929 test7930 test7931 test7932 test7933 test7934 test7935 test7936 test7937 test7938 test7939 test7940 test7941 test7942 test7943 test7944 test7945 test7946 test7947 test7948 test7949 test7950 test7951 test7952 test7953 test7954 test7955 test7956 test7957 test7958 test7959 test7960 test7961 test7962 test7963 test7964 test7965 test7966 test7967 test7968 test7969 test7970 test7971 test7972 test7973 test7974 test7975 test7976 test7977 test7978 test7979 test7980 test7981 test7982 test7983 test7984 test7985 test7986 test7987 test7988 test7989 test7990 test7991 test7992 test7993 test7994 test7995 test7996 test7997 test7998 test7999 test8000 test8001 test8002 test8003 test8004 test8005 test8006 test8007 test8008 test8009 test8010 test8011 test8012 test8013 test8014 test8015 test8016 test8017 test8018 test8019 test8020 test8021 test8022 test8023 test8024 test8025 test8026 test8027 test8028 test8029 test8030 test8031 test8032 test8033 test8034 test8035 test8036 test8037 test8038 test8039 test8040 test8041 test8042 test8043 test8044 test8045 test8046 test8047 test8048 test8049 test8050 test8051 test8052 test8053 test8054 test8055 test8056 test8057 test8058 test8059 test8060 test8061 test8062 test8063 test8064 test8065 test8066 test8067 test8068 test8069 test8070 test8071 test8072 test8073 test8074 test8075 test8076 test8077 test8078 test8079 test8080 test8081 test8082 test8083 test8084 test8085 test8086 test8087 test8088 test8089 test8090 test8091 test8092 test8093 test8094 test8095 test8096 test8097 test8098 test8099 test8100 test8101 test8102 test8103 test8104 test8105 test8106 test8107 test8108 test8109 test8110 test8111 test8112 test8113 test8114 test8115 test8116 test8117 test8118 test8119 test8120 test8121 test8122 test8123 test8124 test8125 test8126 test8127 test8128 test8129 test8130 test8131 test8132 test8133 test8134 test8135 test8136 test8137 test8138 test8139 test8140 test8141 test8142 test8143 test8144 test8145 test8146 test8147 test8148 test8149 test8150 test8151 test8152 test8153 test8154 test8155 test8156 test8157 test8158 test8159 test8160 test8161 test8162 test8163 test8164 test8165 test8166 test8167 test8168 test8169 test8170 test8171 test8172 test8173 test8174 test8175 test8176 test8177 test8178 test8179 test8180 test8181 test8182 test8183 test8184 test8185 test8186 test8187 test8188 test8189 test8190 test8191 test8192 test8193 test8194 test8195 test8196 test8197 test8198 test8199 test8200 test8201 test8202 test8203 test8204 test8205 test8206 test8207 test8208 test8209 test8210 test8211 test8212 test8213 test8214 test8215 test8216 test8217 test8218 test8219 test8220 test8221 test8222 test8223 test8224 test8225 test8226 test8227 test8228 test8229 test8230 test8231 test8232 test8233 test8234 test8235 test8236 test8237 test8238 test8239 test8240 test8241 test8242 test8243 test8244 test8245 test8246 test8247 test8248 test8249 test8250 test8251 test8252 test8253 test8254 test8255 test8256 test8257 test8258 test8259 test8260 test8261 test8262 test8263 test8264 test8265 test8266 test8267 test8268 test8269 test8270 test8271 test8272 test8273 test8274 test8275 test8276 test8277 test8278 test8279 test8280 test8281 test8282 test8283 test8284 test8285 test8286 test8287 test8288 test8289 test8290 test8291 test8292 test8293 test8294 test8295 test8296 test8297 test8298 test8299 test8300 test8301 test8302 test8303 test8304 test8305 test8306 test8307 test8308 test8309 test8310 test8311 test8312 test8313 test8314 test8315 test8316 test8317 test8318 test8319 test8320 test8321 test8322 test8323 test8324 test8325 test8326 test8327 test8328 test8329 test8330 test8331 test8332 test8333 test8334 test8335 test8336 test8337 test8338 test8339 test8340 test8341 test8342 test8343 test8344 test8345 test8346 test8347 test8348 test8349 test8350 test8351 test8352 test8353 test8354 test8355 test8356 test8357 test8358 test8359 test8360 test8361 test8362 test8363 test8364 test8365 test8366 test8367 test8368 test8369 test8370 test8371 test8372 test8373 test8374 test8375 test8376 test8377 test8378 test8379 test8380 test8381 test8382 test8383 test8384 test8385 test8386 test8387 test8388 test8389 test8390 test8391 test8392 test8393 test8394 test8395 test8396 test8397 test8398 test8399 test8400 test8401 test8402 test8403 test8404 test8405 test8406 test8407 test8408 test8409 test8410 test8411 test8412 test8413 test8414 test8415 test8416 test8417 test8418 test8419 test8420 test8421 test8422 test8423 test8424 test8425 test8426 test8427 test8428 test8429 test8430 test8431 test8432 test8433 test8434 test8435 test8436 test8437 test8438 test8439 test8440 test8441 test8442 test8443 test8444 test8445 test8446 test8447 test8448 test8449 test8450 test8451 test8452 test8453 test8454 test8455 test8456 test8457 test8458 test8459 test8460 test8461 test8462 test8463 test8464 test8465 test8466 test8467 test8468 test8469 test8470 test8471 test8472 test8473 test8474 test8475 test8476 test8477 test8478 test8479 test8480 test8481 test8482 test8483 test8484 test8485 test8486 test8487 test8488 test8489 test8490 test8491 test8492 test8493 test8494 test8495 test8496 test8497 test8498 test8499 test8500 test8501 test8502 test8503 test8504 test8505 test8506 test8507 test8508 test8509 test8510 test8511 test8512 test8513 test8514 test8515 test8516 test8517 test8518 test8519 test8520 test8521 test8522 test8523 test8524 test8525 test8526 test8527 test8528 test8529 test8530 test8531 test8532 test8533 test8534 test8535 test8536 test8537 test8538 test8539 test8540 test8541 test8542 test8543 test8544 test8545 test8546 test8547 test8548 test8549 test8550 test8551 test8552 test8553 test8554 test8555 test8556 test8557 test8558 test8559 test8560 test8561 test8562 test8563 test8564 test8565 test8566 test8567 test8568 test8569 test8570 test8571 test8572 test8573 test8574 test8575 test8576 test8577 test8578 test8579 test8580 test8581 test8582 test8583 test8584 test8585 test8586 test8587 test8588 test8589 test8590 test8591 test8592 test8593 test8594 test8595 test8596 test8597 test8598 test8599 test8600 test8601 test8602 test8603 test8604 test8605 test8606 test8607 test8608 test8609 test8610 test8611 test8612 test8613 test8614 test8615 test8616 test8617 test8618 test8619 test8620 test8621 test8622 test8623 test8624 test8625 test8626 test8627 test8628 test8629 test8630 test8631 test8632 test8633 test8634 test8635 test8636 test8637 test8638 test8639 test8640 test8641 test8642 test8643 test8644 test8645 test8646 test8647 test8648 test8649 test8650 test8651 test8652 test8653 test8654 test8655 test8656 test8657 test8658 test8659 test8660 test8661 test8662 test8663 test8664 test8665 test8666 test8667 test8668 test8669 test8670 test8671 test8672 test8673 test8674 test8675 test8676 test8677 test8678 test8679 test8680 test8681 test8682 test8683 test8684 test8685 test8686 test8687 test8688 test8689 test8690 test8691 test8692 test8693 test8694 test8695 test8696 test8697 test8698 test8699 test8700 test8701 test8702 test8703 test8704 test8705 test8706 test8707 test8708 test8709 test8710 test8711 test8712 test8713 test8714 test8715 test8716 test8717 test8718 test8719 test8720 test8721 test8722 test8723 test8724 test8725 test8726 test8727 test8728 test8729 test8730 test8731 test8732 test8733 test8734 test8735 test8736 test8737 test8738 test8739 test8740 test8741 test8742 test8743 test8744 test8745 test8746 test8747 test8748 test8749 test8750 test8751 test8752 test8753 test8754 test8755 test8756 test8757 test8758 test8759 test8760 test8761 test8762 test8763 test8764 test8765 test8766 test8767 test8768 test8769 test8770 test8771 test8772 test8773 test8774 test8775 test8776 test8777 test8778 test8779 test8780 test8781 test8782 test8783 test8784 test8785 test8786 test8787 test8788 test8789 test8790 test8791 test8792 test8793 test8794 test8795 test8796 test8797 test8798 test8799 test8800 test8801 test8802 test8803 test8804 test8805 test8806 test8807 test8808 test8809 test8810 test8811 test8812 test8813 test8814 test8815 test8816 test8817 test8818 test8819 test8820 test8821 test8822 test8823 test8824 test8825 test8826 test8827 test8828 test8829 test8830 test8831 test8832 test8833 test8834 test8835 test8836 test8837 test8838 test8839 test8840 test8841 test8842 test8843 test8844 test8845 test8846 test8847 test8848 test8849 test8850 test8851 test8852 test8853 test8854 test8855 test8856 test8857 test8858 test8859 test8860 test8861 test8862 test8863 test8864 test8865 test8866 test8867 test8868 test8869 test8870 test8871 test8872 test8873 test8874 test8875 test8876 test8877 test8878 test8879 test8880 test8881 test8882 test8883 test8884 test8885 test8886 test8887 test8888 test8889 test8890 test8891 test8892 test8893 test8894 test8895 test8896 test8897 test8898 test8899 test8900 test8901 test8902 test8903 test8904 test8905 test8906 test8907 test8908 test8909 test8910 test8911 test8912 test8913 test8914 test8915 test8916 test8917 test8918 test8919 test8920 test8921 test8922 test8923 test8924 test8925 test8926 test8927 test8928 test8929 test8930 test8931 test8932 test8933 test8934 test8935 test8936 test8937 test8938 test8939 test8940 test8941 test8942 test8943 test8944 test8945 test8946 test8947 test8948 test8949 test8950 test8951 test8952 test8953 test8954 test8955 test8956 test8957 test8958 test8959 test8960 test8961 test8962 test8963 test8964 test8965 test8966 test8967 test8968 test8969 test8970 test8971 test8972 test8973 test8974 test8975 test8976 test8977 test8978 test8979 test8980 test8981 test8982 test8983 test8984 test8985 test8986 test8987 test8988 test8989 test8990 test8991 test8992 test8993 test8994 test8995 test8996 test8997 test8998 test8999 test9000 test9001 test9002 test9003 test9004 test9005 test9006 test9007 test9008 test9009 test9010 test9011 test9012 test9013 test9014 test9015 test9016 test9017 test9018 test9019 test9020 test9021 test9022 test9023 test9024 test9025 test9026 test9027 test9028 test9029 test9030 test9031 test9032 test9033 test9034 test9035 test9036 test9037 test9038 test9039 test9040 test9041 test9042 test9043 test9044 test9045 test9046 test9047 test9048 test9049 test9050 test9051 test9052 test9053 test9054 test9055 test9056 test9057 test9058 test9059 test9060 test9061 test9062 test9063 test9064 test9065 test9066 test9067 test9068 test9069 test9070 test9071 test9072 test9073 test9074 test9075 test9076 test9077 test9078 test9079 test9080 test9081 test9082 test9083 test9084 test9085 test9086 test9087 test9088 test9089 test9090 test9091 test9092 test9093 test9094 test9095 test9096 test9097 test9098 test9099 test9100 test9101 test9102 test9103 test9104 test9105 test9106 test9107 test9108 test9109 test9110 test9111 test9112 test9113 test9114 test9115 test9116 test9117 test9118 test9119 test9120 test9121 test9122 test9123 test9124 test9125 test9126 test9127 test9128 test9129 test9130 test9131 test9132 test9133 test9134 test9135 test9136 test9137 test9138 test9139 test9140 test9141 test9142 test9143 test9144 test9145 test9146 test9147 test9148 test9149 test9150 test9151 test9152 test9153 test9154 test9155 test9156 test9157 test9158 test9159 test9160 test9161 test9162 test9163 test9164 test9165 test9166 test9167 test9168 test9169 test9170 test9171 test9172 test9173 test9174 test9175 test9176 test9177 test9178 test9179 test9180 test9181 test9182 test9183 test9184 test9185 test9186 test9187 test9188 test9189 test9190 test9191 test9192 test9193 test9194 test9195 test9196 test9197 test9198 test9199 test9200 test9201 test9202 test9203 test9204 test9205 test9206 test9207 test9208 test9209 test9210 test9211 test9212 test9213 test9214 test9215 test9216 test9217 test9218 test9219 test9220 test9221 test9222 test9223 test9224 test9225 test9226 test9227 test9228 test9229 test9230 test9231 test9232 test9233 test9234 test9235 test9236 test9237 test9238 test9239 test9240 test9241 test9242 test9243 test9244 test9245 test9246 test9247 test9248 test9249 test9250 test9251 test9252 test9253 test9254 test9255 test9256 test9257 test9258 test9259 test9260 test9261 test9262 test9263 test9264 test9265 test9266 test9267 test9268 test9269 test9270 test9271 test9272 test9273 test9274 test9275 test9276 test9277 test9278 test9279 test9280 test9281 test9282 test9283 test9284 test9285 test9286 test9287 test9288 test9289 test9290 test9291 test9292 test9293 test9294 test9295 test9296 test9297 test9298 test9299 test9300 test9301 test9302 test9303 test9304 test9305 test9306 test9307 test9308 test9309 test9310 test9311 test9312 test9313 test9314 test9315 test9316 test9317 test9318 test9319 test9320 test9321 test9322 test9323 test9324 test9325 test9326 test9327 test9328 test9329 test9330 test9331 test9332 test9333 test9334 test9335 test9336 test9337 test9338 test9339 test9340 test9341 test9342 test9343 test9344 test9345 test9346 test9347 test9348 test9349 test9350 test9351 test9352 test9353 test9354 test9355 test9356 test9357 test9358 test9359 test9360 test9361 test9362 test9363 test9364 test9365 test9366 test9367 test9368 test9369 test9370 test9371 test9372 test9373 test9374 test9375 test9376 test9377 test9378 test9379 test9380 test9381 test9382 test9383 test9384 test9385 test9386 test9387 test9388 test9389 test9390 test9391 test9392 test9393 test9394 test9395 test9396 test9397 test9398 test9399 test9400 test9401 test9402 test9403 test9404 test9405 test9406 test9407 test9408 test9409 test9410 test9411 test9412 test9413 test9414 test9415 test9416 test9417 test9418 test9419 test9420 test9421 test9422 test9423 test9424 test9425 test9426 test9427 test9428 test9429 test9430 test9431 test9432 test9433 test9434 test9435 test9436 test9437 test9438 test9439 test9440 test9441 test9442 test9443 test9444 test9445 test9446 test9447 test9448 test9449 test9450 test9451 test9452 test9453 test9454 test9455 test9456 test9457 test9458 test9459 test9460 test9461 test9462 test9463 test9464 test9465 test9466 test9467 test9468 test9469 test9470 test9471 test9472 test9473 test9474 test9475 test9476 test9477 test9478 test9479 test9480 test9481 test9482 test9483 test9484 test9485 test9486 test9487 test9488 test9489 test9490 test9491 test9492 test9493 test9494 test9495 test9496 test9497 test9498 test9499 test9500 test9501 test9502 test9503 test9504 test9505 test9506 test9507 test9508 test9509 test9510 test9511 test9512 test9513 test9514 test9515 test9516 test9517 test9518 test9519 test9520 test9521 test9522 test9523 test9524 test9525 test9526 test9527 test9528 test9529 test9530 test9531 test9532 test9533 test9534 test9535 test9536 test9537 test9538 test9539 test9540 test9541 test9542 test9543 test9544 test9545 test9546 test9547 test9548 test9549 test9550 test9551 test9552 test9553 test9554 test9555 test9556 test9557 test9558 test9559 test9560 test9561 test9562 test9563 test9564 test9565 test9566 test9567 test9568 test9569 test9570 test9571 test9572 test9573 test9574 test9575 test9576 test9577 test9578 test9579 test9580 test9581 test9582 test9583 test9584 test9585 test9586 test9587 test9588 test9589 test9590 test9591 test9592 test9593 test9594 test9595 test9596 test9597 test9598 test9599 test9600 test9601 test9602 test9603 test9604 test9605 test9606 test9607 test9608 test9609 test9610 test9611 test9612 test9613 test9614 test9615 test9616 test9617 test9618 test9619 test9620 test9621 test9622 test9623 test9624 test9625 test9626 test9627 test9628 test9629 test9630 test9631 test9632 test9633 test9634 test9635 test9636 test9637 test9638 test9639 test9640 test9641 test9642 test9643 test9644 test9645 test9646 test9647 test9648 test9649 test9650 test9651 test9652 test9653 test9654 test9655 test9656 test9657 test9658 test9659 test9660 test9661 test9662 test9663 test9664 test9665 test9666 test9667 test9668 test9669 test9670 test9671 test9672 test9673 test9674 test9675 test9676 test9677 test9678 test9679 test9680 test9681 test9682 test9683 test9684 test9685 test9686 test9687 test9688 test9689 test9690 test9691 test9692 test9693 test9694 test9695 test9696 test9697 test9698 test9699 test9700 test9701 test9702 test9703 test9704 test9705 test9706 test9707 test9708 test9709 test9710 test9711 test9712 test9713 test9714 test9715 test9716 test9717 test9718 test9719 test9720 test9721 test9722 test9723 test9724 test9725 test9726 test9727 test9728 test9729 test9730 test9731 test9732 test9733 test9734 test9735 test9736 test9737 test9738 test9739 test9740 test9741 test9742 test9743 test9744 test9745 test9746 test9747 test9748 test9749 test9750 test9751 test9752 test9753 test9754 test9755 test9756 test9757 test9758 test9759 test9760 test9761 test9762 test9763 test9764 test9765 test9766 test9767 test9768 test9769 test9770 test9771 test9772 test9773 test9774 test9775 test9776 test9777 test9778 test9779 test9780 test9781 test9782 test9783 test9784 test9785 test9786 test9787 test9788 test9789 test9790 test9791 test9792 test9793 test9794 test9795 test9796 test9797 test9798 test9799 test9800 test9801 test9802 test9803 test9804 test9805 test9806 test9807 test9808 test9809 test9810 test9811 test9812 test9813 test9814 test9815 test9816 test9817 test9818 test9819 test9820 test9821 test9822 test9823 test9824 test9825 test9826 test9827 test9828 test9829 test9830 test9831 test9832 test9833 test9834 test9835 test9836 test9837 test9838 test9839 test9840 test9841 test9842 test9843 test9844 test9845 test9846 test9847 test9848 test9849 test9850 test9851 test9852 test9853 test9854 test9855 test9856 test9857 test9858 test9859 test9860 test9861 test9862 test9863 test9864 test9865 test9866 test9867 test9868 test9869 test9870 test9871 test9872 test9873 test9874 test9875 test9876 test9877 test9878 test9879 test9880 test9881 test9882 test9883 test9884 test9885 test9886 test9887 test9888 test9889 test9890 test9891 test9892 test9893 test9894 test9895 test9896 test9897 test9898 test9899 test9900 test9901 test9902 test9903 test9904 test9905 test9906 test9907 test9908 test9909 test9910 test9911 test9912 test9913 test9914 test9915 test9916 test9917 test9918 test9919 test9920 test9921 test9922 test9923 test9924 test9925 test9926 test9927 test9928 test9929 test9930 test9931 test9932 test9933 test9934 test9935 test9936 test9937 test9938 test9939 test9940 test9941 test9942 test9943 test9944 test9945 test9946 test9947 test9948 test9949 test9950 test9951 test9952 test9953 test9954 test9955 test9956 test9957 test9958 test9959 test9960 test9961 test9962 test9963 test9964 test9965 test9966 test9967 test9968 test9969 test9970 test9971 test9972 test9973 test9974 test9975 test9976 test9977 test9978 test9979 test9980 test9981 test9982 test9983 test9984 test9985 test9986 test9987 test9988 test9989 test9990 test9991 test9992 test9993 test9994 test9995 test9996 test9997 test9998 test9999 test10000 } } - """ - - test "long query" do - {time, {:ok, _}} = :timer.tc(&Absinthe.Lexer.tokenize/1, [@query]) - IO.puts("long query lexer time was #{time / 1_000_000} sec") + @tag timeout: 3_000 + test "long query doesn't take too long" do + many_directives = String.duplicate("@abc ", 10_000) + {:ok, _} = Absinthe.Lexer.tokenize("{ __typename #{many_directives} }") end end From f3d954fc13952ae20380ff6d918db1db520a4070 Mon Sep 17 00:00:00 2001 From: Ellie Poley Date: Wed, 4 Jan 2023 18:15:39 -0600 Subject: [PATCH 060/120] Comment new lexer perf test --- test/absinthe/lexer_test.exs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/test/absinthe/lexer_test.exs b/test/absinthe/lexer_test.exs index 8a8961a6c5..409bea9b62 100644 --- a/test/absinthe/lexer_test.exs +++ b/test/absinthe/lexer_test.exs @@ -97,6 +97,10 @@ defmodule Absinthe.LexerTest do @tag timeout: 3_000 test "long query doesn't take too long" do + # This tests the performance of long queries. Before optimization work, this + # test took 16 seconds. After optimization it took 0.08 seconds. Setting + # a generous ExUnit timeout ensures there has not been a performance regression + # while hopefully preventing testing fragility. many_directives = String.duplicate("@abc ", 10_000) {:ok, _} = Absinthe.Lexer.tokenize("{ __typename #{many_directives} }") end From d6fc14ad28ceb48e2dd2990a348ae8c1b2440b98 Mon Sep 17 00:00:00 2001 From: Ellie Poley Date: Thu, 5 Jan 2023 16:11:08 -0600 Subject: [PATCH 061/120] small stylistic tweak to be symmetrical with the nearby function head --- lib/absinthe/lexer.ex | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/absinthe/lexer.ex b/lib/absinthe/lexer.ex index 80f1bfedde..dad0937a3a 100644 --- a/lib/absinthe/lexer.ex +++ b/lib/absinthe/lexer.ex @@ -299,7 +299,7 @@ defmodule Absinthe.Lexer do %{line_num_cursor: line_num_cursor} = cursor_state, token_line_num ) - when token_line_num == line_num_cursor, + when line_num_cursor == token_line_num, do: cursor_state defp maybe_move_cursor_to_next_line( From c1a837739169f155ea99b852aa087ff175d1b245 Mon Sep 17 00:00:00 2001 From: Bernard Duggan Date: Thu, 12 Jan 2023 12:13:43 +1100 Subject: [PATCH 062/120] Add pipeline modifier option to Absinthe.run --- lib/absinthe.ex | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/lib/absinthe.ex b/lib/absinthe.ex index 122afb8bb0..5ed484cdc2 100644 --- a/lib/absinthe.ex +++ b/lib/absinthe.ex @@ -49,6 +49,8 @@ defmodule Absinthe do | %{data: nil | result_selection_t, errors: [result_error_t]} | %{errors: [result_error_t]} + @type pipeline_modifier_fun :: (Absinthe.Pipeline.t(), Keyword.t() -> Absinthe.Pipeline.t()) + @doc """ Evaluates a query document against a schema, with options. @@ -92,7 +94,8 @@ defmodule Absinthe do operation_name: String.t(), analyze_complexity: boolean, variables: %{optional(String.t()) => any()}, - max_complexity: non_neg_integer | :infinity + max_complexity: non_neg_integer | :infinity, + pipeline_modifier: pipeline_modifier_fun() ] @type run_result :: {:ok, result_t} | {:error, String.t()} @@ -103,9 +106,12 @@ defmodule Absinthe do run_opts ) :: run_result def run(document, schema, options \\ []) do + pipeline_modifier = options[:pipeline_modifier] || (&pipeline_identity/2) + pipeline = schema |> Absinthe.Pipeline.for_document(options) + |> pipeline_modifier.(options) case Absinthe.Pipeline.run(document, pipeline) do {:ok, %{result: result}, _phases} -> @@ -126,7 +132,7 @@ defmodule Absinthe do @spec run!( binary | Absinthe.Language.Source.t() | Absinthe.Language.Document.t(), Absinthe.Schema.t(), - Keyword.t() + run_opts ) :: result_t | no_return def run!(input, schema, options \\ []) do case run(input, schema, options) do @@ -134,4 +140,6 @@ defmodule Absinthe do {:error, err} -> raise ExecutionError, message: err end end + + defp pipeline_identity(pipeline, _options), do: pipeline end From 082025db8dbd982bcf02b0331758b4b0f4f3ca39 Mon Sep 17 00:00:00 2001 From: Ellie Poley Date: Thu, 12 Jan 2023 11:08:46 -0600 Subject: [PATCH 063/120] Use Enum.map_reduce, it's a little cleaner --- lib/absinthe/lexer.ex | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/absinthe/lexer.ex b/lib/absinthe/lexer.ex index dad0937a3a..f293e0dabf 100644 --- a/lib/absinthe/lexer.ex +++ b/lib/absinthe/lexer.ex @@ -251,7 +251,7 @@ defmodule Absinthe.Lexer do next_lines: next_lines } - Enum.reduce(tokens, {[], initial_cursor_state}, fn current_token, {results, cursor_state} -> + Enum.map_reduce(tokens, initial_cursor_state, fn current_token, cursor_state -> {token_line_num, token_byte_col} = case current_token do {_, {token_line_num, token_byte_col}, _} -> {token_line_num, token_byte_col} @@ -288,10 +288,10 @@ defmodule Absinthe.Lexer do {ident, _} -> {ident, {token_line_num, token_char_col}} end - {[result | results], next_cursor_state} + {result, next_cursor_state} end) |> case do - {results, _} -> Enum.reverse(results) + {results, _} -> results end end From 3c3e5ea3f21a62115e7e62d7e0c76a07ddf8af87 Mon Sep 17 00:00:00 2001 From: Ellie Poley Date: Mon, 23 Jan 2023 11:40:08 -0600 Subject: [PATCH 064/120] Fix a bug the compiler caught --- lib/absinthe/lexer.ex | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/absinthe/lexer.ex b/lib/absinthe/lexer.ex index 76ffe2cfdb..556dc4000f 100644 --- a/lib/absinthe/lexer.ex +++ b/lib/absinthe/lexer.ex @@ -347,7 +347,7 @@ defmodule Absinthe.Lexer do {rest, [{:name, line_and_column(loc, byte_offset, length(value)), value}], context} end - defp labeled_token(_, _, %{token_count: count, token_limit: limit} = _context, _, _) + defp labeled_token(_, _, %{token_count: count, token_limit: limit} = _context, _, _, _) when count >= limit, do: {:error, :stopped_at_token_limit} From 22fa9e9f2cf8ca0b98ec5888bdc52fa1981a198d Mon Sep 17 00:00:00 2001 From: Ellie Poley Date: Mon, 23 Jan 2023 11:40:49 -0600 Subject: [PATCH 065/120] In light of PR #1220, the default limit should now be infinity --- guides/complexity-analysis.md | 9 ++++----- lib/absinthe/lexer.ex | 2 +- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/guides/complexity-analysis.md b/guides/complexity-analysis.md index f370cbc9cf..96713007ad 100644 --- a/guides/complexity-analysis.md +++ b/guides/complexity-analysis.md @@ -121,8 +121,7 @@ calculated and maximum complexities. ## Token Limits To protect a service from invalid queries that could take considerable resources to parse, -Absinthe includes a maximum limit on tokens in the GraphQL request document. If the lexer -encounters more tokens than this, it will stop the parse phase and return a phase error -with the message `"Token limit exceeded"`. This limit is 15,000 by default and can be -overridden by providing the option `token_limit` to `Absinthe.run`. `token_limit` could -be an integer or `:infinity` for no limit. +Absinthe offers the option to configure a maximum limit on tokens in the GraphQL request document. +If the lexer encounters more tokens than this, it will stop the parse phase and return a phase error +with the message `"Token limit exceeded"`. This limit is `:infinity` by default (no limit) +and can be overridden by providing an integer to the option `token_limit` to `Absinthe.run`. diff --git a/lib/absinthe/lexer.ex b/lib/absinthe/lexer.ex index 556dc4000f..ba8ef27e1e 100644 --- a/lib/absinthe/lexer.ex +++ b/lib/absinthe/lexer.ex @@ -232,7 +232,7 @@ defmodule Absinthe.Lexer do def tokenize(input, options \\ []) do lines = String.split(input, ~r/\r?\n/) - tokenize_opts = [context: %{token_limit: Keyword.get(options, :token_limit, 15_000)}] + tokenize_opts = [context: %{token_limit: Keyword.get(options, :token_limit, :infinity)}] case do_tokenize(input, tokenize_opts) do {:error, :stopped_at_token_limit, _, _, _, _} -> From 957668781b3407f8b9468f3e8c46e609b7ad8558 Mon Sep 17 00:00:00 2001 From: Jerome Ueberschlag <2552444+jueberschlag@users.noreply.github.com> Date: Tue, 24 Jan 2023 22:06:51 +0100 Subject: [PATCH 066/120] Propagate errors if a non-null list of non-null elements contains a null element --- .../phase/document/execution/resolution.ex | 15 ++- .../phase/execution/non_null_test.exs | 91 +++++++++++++++++-- 2 files changed, 92 insertions(+), 14 deletions(-) diff --git a/lib/absinthe/phase/document/execution/resolution.ex b/lib/absinthe/phase/document/execution/resolution.ex index d15ddd9cc1..0fc2d3a222 100644 --- a/lib/absinthe/phase/document/execution/resolution.ex +++ b/lib/absinthe/phase/document/execution/resolution.ex @@ -283,6 +283,15 @@ defmodule Absinthe.Phase.Document.Execution.Resolution do |> propagate_null_trimming end + defp maybe_add_non_null_error([], values, %Type.NonNull{of_type: %Type.List{}}) do + values + |> Enum.with_index() + |> Enum.filter(&is_nil(elem(&1, 0))) + |> Enum.map(fn {_value, index} -> + %{message: "Cannot return null for non-nullable field", path: [index]} + end) + end + defp maybe_add_non_null_error([], nil, %Type.NonNull{}) do ["Cannot return null for non-nullable field"] end @@ -312,11 +321,7 @@ defmodule Absinthe.Phase.Document.Execution.Resolution do nil |> to_result(bp_field, full_type, node.extensions) - |> Map.put(:errors, bad_child.errors) - - # ^ We don't have to worry about clobbering the current node's errors because, - # if it had any errors, it wouldn't have any children and we wouldn't be - # here anyway. + |> Map.put(:errors, node.errors ++ bad_child.errors) else node end diff --git a/test/absinthe/phase/execution/non_null_test.exs b/test/absinthe/phase/execution/non_null_test.exs index 0895988574..9d62e41fd0 100644 --- a/test/absinthe/phase/execution/non_null_test.exs +++ b/test/absinthe/phase/execution/non_null_test.exs @@ -16,6 +16,18 @@ defmodule Absinthe.Phase.Document.Execution.NonNullTest do {:ok, %{}} end + defp things_resolver(_, %{make_null: make_null}, _) do + if make_null do + {:ok, [nil]} + else + {:ok, [%{}]} + end + end + + defp things_resolver(_, _, _) do + {:ok, [%{}]} + end + object :thing do field :nullable, :thing do arg :make_null, :boolean @@ -38,6 +50,11 @@ defmodule Absinthe.Phase.Document.Execution.NonNullTest do {:error, "boom"} end end + + field :non_null_list_of_non_null, non_null(list_of(non_null(:thing))) do + arg :make_null, :boolean + resolve &things_resolver/3 + end end query do @@ -53,21 +70,16 @@ defmodule Absinthe.Phase.Document.Execution.NonNullTest do end field :nullable_list_of_nullable, list_of(:thing) do - resolve fn _, _ -> - {:ok, [%{}]} - end + resolve &things_resolver/3 end field :nullable_list_of_non_null, list_of(non_null(:thing)) do - resolve fn _, _ -> - {:ok, [%{}]} - end + resolve &things_resolver/3 end field :non_null_list_of_non_null, non_null(list_of(non_null(:thing))) do - resolve fn _, _ -> - {:ok, [%{}]} - end + arg :make_null, :boolean + resolve &things_resolver/3 end @desc """ @@ -250,5 +262,66 @@ defmodule Absinthe.Phase.Document.Execution.NonNullTest do assert {:ok, %{data: data, errors: errors}} == Absinthe.run(doc, Schema) end + + test "list of non null things works when child is null" do + doc = """ + { + nonNullListOfNonNull(makeNull: true) { __typename } + } + """ + + data = nil + + errors = [ + %{ + locations: [%{column: 3, line: 2}], + message: "Cannot return null for non-nullable field", + path: ["nonNullListOfNonNull", 0] + } + ] + + assert {:ok, %{data: data, errors: errors}} == Absinthe.run(doc, Schema) + end + + test "returning null from a non null list makes the parent nullable null at arbitrary depth" do + doc = """ + { + nullableListOfNonNull { + nonNullListOfNonNull { + nonNullListOfNonNull { + nonNullListOfNonNull { + nonNullListOfNonNull(makeNull: true) { __typename } + } + } + } + } + } + """ + + data = %{"nullableListOfNonNull" => nil} + + path = [ + "nullableListOfNonNull", + 0, + "nonNullListOfNonNull", + 0, + "nonNullListOfNonNull", + 0, + "nonNullListOfNonNull", + 0, + "nonNullListOfNonNull", + 0 + ] + + errors = [ + %{ + locations: [%{column: 11, line: 6}], + message: "Cannot return null for non-nullable field", + path: path + } + ] + + assert {:ok, %{data: data, errors: errors}} == Absinthe.run(doc, Schema) + end end end From 986409450056afbee74a6acb8f58f4492c954f63 Mon Sep 17 00:00:00 2001 From: Ben Wilson Date: Wed, 1 Feb 2023 12:21:56 -0500 Subject: [PATCH 067/120] bump to 1.7.1 --- CHANGELOG.md | 4 +++- mix.exs | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b4f868c98e..9ea9dc023e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,6 @@ # Changelog -## Unreleased +## 1.7.1 - Bug Fix: [Validate field identifier uniqueness](https://github.com/absinthe-graphql/absinthe/pull/1200) - Bug Fix: [Validate type references for invalid wrapped types](https://github.com/absinthe-graphql/absinthe/pull/1195) - Breaking Bugfix: [Validate repeatable directives on schemas](https://github.com/absinthe-graphql/absinthe/pull/1179) @@ -17,6 +17,8 @@ - Feature: [Add `import_directives` macro](https://github.com/absinthe-graphql/absinthe/pull/1158) - Feature: [Support type extensions on schema declarations](https://github.com/absinthe-graphql/absinthe/pull/1176) - Bug Fix: [Root objects are marked as referenced correctly](https://github.com/absinthe-graphql/absinthe/pull/1186) +- Bug Fix: [Prevent DDOS attacks with long queries](https://github.com/absinthe-graphql/absinthe/pull/1220) +- Feature: [pipeline_modifier option to Absinthe.run/3](https://github.com/absinthe-graphql/absinthe/pull/1221) ## 1.7.0 diff --git a/mix.exs b/mix.exs index 8641e7d8e9..2309e8d1c7 100644 --- a/mix.exs +++ b/mix.exs @@ -2,7 +2,7 @@ defmodule Absinthe.Mixfile do use Mix.Project @source_url "https://github.com/absinthe-graphql/absinthe" - @version "1.7.0" + @version "1.7.1" def project do [ From 1562385a0a76ab68fe3dd963b958ca22ccd4608a Mon Sep 17 00:00:00 2001 From: Ben Wilson Date: Mon, 6 Feb 2023 23:39:01 -0500 Subject: [PATCH 068/120] Update CHANGELOG.md Highlight breaking bug fixes more obviously. --- CHANGELOG.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9ea9dc023e..45c0aeb462 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,9 +1,10 @@ # Changelog ## 1.7.1 +- Breaking Bugfix: [Validate repeatable directives on schemas](https://github.com/absinthe-graphql/absinthe/pull/1179) +- Breaking Bugfix: [Add "Objects must define fields" schema validation](https://github.com/absinthe-graphql/absinthe/pull/1167) - Bug Fix: [Validate field identifier uniqueness](https://github.com/absinthe-graphql/absinthe/pull/1200) - Bug Fix: [Validate type references for invalid wrapped types](https://github.com/absinthe-graphql/absinthe/pull/1195) -- Breaking Bugfix: [Validate repeatable directives on schemas](https://github.com/absinthe-graphql/absinthe/pull/1179) - Bug Fix: Adds **optional fix** for non compliant built-in scalar Int type. `use Absinthe.Schema, use_spec_compliant_int_scalar: true` in your schema to use the fixed Int type. It is also advisable to upgrade for custom types if you are leveraging the use of integers outside the GraphQl standard. [#1131](https://github.com/absinthe-graphql/absinthe/pull/1131). - Feature: [Support error tuples when scalar parsing fails](https://github.com/absinthe-graphql/absinthe/pull/1187) - Feature: [Convert SDL Language.\* structs to SDL notation](https://github.com/absinthe-graphql/absinthe/pull/1160) @@ -13,7 +14,6 @@ - Bug Fix: [Add `__private__` field to EnumValueDefinition](https://github.com/absinthe-graphql/absinthe/pull/1148) - Bug Fix: [Fix bug in Schema.**absinthe_types**(:all) for Persistent Term](https://github.com/absinthe-graphql/absinthe/pull/1161) - Bug Fix: [Fix default enum value check for SDL schema's](https://github.com/absinthe-graphql/absinthe/pull/1188) -- Bug Fix: [Add "Objects must define fields" schema validation](https://github.com/absinthe-graphql/absinthe/pull/1167) - Feature: [Add `import_directives` macro](https://github.com/absinthe-graphql/absinthe/pull/1158) - Feature: [Support type extensions on schema declarations](https://github.com/absinthe-graphql/absinthe/pull/1176) - Bug Fix: [Root objects are marked as referenced correctly](https://github.com/absinthe-graphql/absinthe/pull/1186) From c84b8ad1369a9df5d1c62b587cd18facfd43f4ab Mon Sep 17 00:00:00 2001 From: Saverio Date: Wed, 8 Feb 2023 11:30:13 +0100 Subject: [PATCH 069/120] Support propagating OTel context --- guides/telemetry.md | 26 +++++++++++++++++++++++++ lib/absinthe/middleware/async.ex | 11 ++++++++++- lib/absinthe/middleware/batch.ex | 11 ++++++++++- mix.exs | 1 + mix.lock | 2 ++ test/absinthe/middleware/async_test.exs | 18 +++++++++++++++++ test/absinthe/middleware/batch_test.exs | 22 +++++++++++++++++++++ 7 files changed, 89 insertions(+), 2 deletions(-) diff --git a/guides/telemetry.md b/guides/telemetry.md index 1a1b62bb4c..304691ef3e 100644 --- a/guides/telemetry.md +++ b/guides/telemetry.md @@ -62,3 +62,29 @@ After a query is executed, you'll see something like: } } ``` + +## Opentelemetry + +When using Opentelemetry, one usually wants to correlate spans that are created +in spawned tasks with the main trace. For example, you might have a trace started +in a Phoenix endpoint, and then have spans around database access. + +One can correlate manually by attaching the OTel context the task function: + +```elixir +ctx = OpenTelemetry.Ctx.get_current() + +Task.async(fn -> + OpenTelemetry.Ctx.attach(ctx) + + # do stuff that might create spans +end) +``` + +When using the `async` and `batch` middleware, the tasks are spawned by Absinthe, +so you can't attach the context manually. + +Instead, you can add the `:opentelemetry_process_propagator` package to your +dependencies, which has a `Task.async/1` wrapper that will attach the context +automatically. If the package is installed, the middleware will use it in place +of the default `Task.async/1`. diff --git a/lib/absinthe/middleware/async.ex b/lib/absinthe/middleware/async.ex index b56c95d57c..c121af0481 100644 --- a/lib/absinthe/middleware/async.ex +++ b/lib/absinthe/middleware/async.ex @@ -52,7 +52,7 @@ defmodule Absinthe.Middleware.Async do # task so we have actual data. Thus, we prepend this module to the middleware stack. def call(%{state: :unresolved} = res, {fun, opts}) when is_function(fun) do task = - Task.async(fn -> + async(fn -> :telemetry.span([:absinthe, :middleware, :async, :task], %{}, fn -> {fun.(), %{}} end) end) @@ -110,4 +110,13 @@ defmodule Absinthe.Middleware.Async do pipeline end end + + # Optionally use `async/1` function from `opentelemetry_process_propagator` if available + if Code.ensure_loaded?(OpentelemetryProcessPropagator.Task) do + @spec async((() -> any)) :: Task.t() + defdelegate async(fun), to: OpentelemetryProcessPropagator.Task + else + @spec async((() -> any)) :: Task.t() + defdelegate async(fun), to: Task + end end diff --git a/lib/absinthe/middleware/batch.ex b/lib/absinthe/middleware/batch.ex index e6334feafc..4ab60719b1 100644 --- a/lib/absinthe/middleware/batch.ex +++ b/lib/absinthe/middleware/batch.ex @@ -137,7 +137,7 @@ defmodule Absinthe.Middleware.Batch do start_time_mono = System.monotonic_time() task = - Task.async(fn -> + async(fn -> {batch_fun, call_batch_fun(batch_fun, batch_data)} end) @@ -206,4 +206,13 @@ defmodule Absinthe.Middleware.Batch do pipeline end end + + # Optionally use `async/1` function from `opentelemetry_process_propagator` if available + if Code.ensure_loaded?(OpentelemetryProcessPropagator.Task) do + @spec async((() -> any)) :: Task.t() + defdelegate async(fun), to: OpentelemetryProcessPropagator.Task + else + @spec async((() -> any)) :: Task.t() + defdelegate async(fun), to: Task + end end diff --git a/mix.exs b/mix.exs index 2309e8d1c7..7b2361679f 100644 --- a/mix.exs +++ b/mix.exs @@ -75,6 +75,7 @@ defmodule Absinthe.Mixfile do {:telemetry, "~> 1.0 or ~> 0.4"}, {:dataloader, "~> 1.0.0", optional: true}, {:decimal, "~> 1.0 or ~> 2.0", optional: true}, + {:opentelemetry_process_propagator, "~> 0.2.1", optional: true}, {:ex_doc, "~> 0.22", only: :dev}, {:benchee, ">= 1.0.0", only: :dev}, {:dialyxir, "~> 1.1.0", only: [:dev, :test], runtime: false}, diff --git a/mix.lock b/mix.lock index 3845cebc01..a956f2cf5c 100644 --- a/mix.lock +++ b/mix.lock @@ -14,5 +14,7 @@ "makeup_graphql": {:hex, :makeup_graphql, "0.1.2", "81e2939aab6d2b81d39ee5d9e13fae02599e9ca6e1152e0eeed737a98a5f96aa", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}, {:nimble_parsec, "~> 1.1", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "3390ab04ba388d52a94bbe64ef62aa4d7923ceaffac43ec948f58f631440e8fb"}, "mix_test_watch": {:hex, :mix_test_watch, "1.0.2", "34900184cbbbc6b6ed616ed3a8ea9b791f9fd2088419352a6d3200525637f785", [:mix], [{:file_system, "~> 0.2.1 or ~> 0.3", [hex: :file_system, repo: "hexpm", optional: false]}], "hexpm", "47ac558d8b06f684773972c6d04fcc15590abdb97aeb7666da19fcbfdc441a07"}, "nimble_parsec": {:hex, :nimble_parsec, "1.2.3", "244836e6e3f1200c7f30cb56733fd808744eca61fd182f731eac4af635cc6d0b", [:mix], [], "hexpm", "c8d789e39b9131acf7b99291e93dae60ab48ef14a7ee9d58c6964f59efb570b0"}, + "opentelemetry_api": {:hex, :opentelemetry_api, "1.2.0", "454a35655b4c1924405ef1f3587f2c6f141bf73366b2c5e8a38dcc619b53eaa0", [:mix, :rebar3], [], "hexpm", "9e677c68243de0f70538798072e66e1fb1d4a2ca8888a6eb493c0a41e5480c35"}, + "opentelemetry_process_propagator": {:hex, :opentelemetry_process_propagator, "0.2.1", "20ac37648faf7175cade16fda8d58e6f1ff1b7f2a50a8ef9d70a032c41aba315", [:mix, :rebar3], [{:opentelemetry_api, "~> 1.0", [hex: :opentelemetry_api, repo: "hexpm", optional: false]}], "hexpm", "f317237e39636d4f6140afa5d419e85ed3dc9e9a57072e7cd442df42af7b8aac"}, "telemetry": {:hex, :telemetry, "0.4.3", "a06428a514bdbc63293cd9a6263aad00ddeb66f608163bdec7c8995784080818", [:rebar3], [], "hexpm", "eb72b8365ffda5bed68a620d1da88525e326cb82a75ee61354fc24b844768041"}, } diff --git a/test/absinthe/middleware/async_test.exs b/test/absinthe/middleware/async_test.exs index 49e39a196e..e797c90838 100644 --- a/test/absinthe/middleware/async_test.exs +++ b/test/absinthe/middleware/async_test.exs @@ -48,6 +48,14 @@ defmodule Absinthe.Middleware.AsyncTest do {:middleware, Elixir.Absinthe.Middleware.Async, task} end end + + field :async_check_otel_ctx, :string do + resolve fn _, _, _ -> + async(fn -> + {:ok, OpenTelemetry.Ctx.get_value("stored_value", nil)} + end) + end + end end def cool_async(fun) do @@ -125,4 +133,14 @@ defmodule Absinthe.Middleware.AsyncTest do assert {:ok, %{data: %{"returnsNil" => nil}}} == Absinthe.run(doc, Schema) end + + test "propagates the OTel context" do + doc = """ + {asyncCheckOtelCtx} + """ + + OpenTelemetry.Ctx.set_value("stored_value", "some_value") + + assert {:ok, %{data: %{"asyncCheckOtelCtx" => "some_value"}}} == Absinthe.run(doc, Schema) + end end diff --git a/test/absinthe/middleware/batch_test.exs b/test/absinthe/middleware/batch_test.exs index d099055649..11b9654ddb 100644 --- a/test/absinthe/middleware/batch_test.exs +++ b/test/absinthe/middleware/batch_test.exs @@ -52,11 +52,23 @@ defmodule Absinthe.Middleware.BatchTest do end) end end + + field :ctx, :string do + resolve fn _, _, _ -> + batch({__MODULE__, :otel_ctx}, nil, fn batch -> + {:ok, batch} + end) + end + end end def by_id(_, ids) do Map.take(@organizations, ids) end + + def otel_ctx(_, _) do + OpenTelemetry.Ctx.get_value("stored_value", nil) + end end test "can resolve a field using the normal async helper" do @@ -128,4 +140,14 @@ defmodule Absinthe.Middleware.BatchTest do assert_receive {:telemetry_event, [:absinthe, :middleware, :batch, :stop], %{duration: _}, %{id: _, batch_fun: _, batch_opts: _, batch_data: _, result: _}} end + + test "propagates the OTel context" do + doc = """ + {ctx} + """ + + OpenTelemetry.Ctx.set_value("stored_value", "some_value") + + assert {:ok, %{data: %{"ctx" => "some_value"}}} == Absinthe.run(doc, Schema) + end end From 57a87c291a678868ee15931642842bfc8411a53b Mon Sep 17 00:00:00 2001 From: Maarten van Vliet Date: Fri, 10 Feb 2023 21:48:41 +0100 Subject: [PATCH 070/120] Type extensions objects can be empty See the spec https://spec.graphql.org/October2021/#sec-Object-Extensions an object extending another object can be empty. This is useful for adding directives to an object without adding any fields. It came up in a slack conversation that this was an issue --- .../validation/object_must_define_fields.ex | 4 ++++ .../rule/object_must_define_fields_test.exs | 20 +++++++++++++++++++ 2 files changed, 24 insertions(+) diff --git a/lib/absinthe/phase/schema/validation/object_must_define_fields.ex b/lib/absinthe/phase/schema/validation/object_must_define_fields.ex index c1e6899bd6..1871bc3e3d 100644 --- a/lib/absinthe/phase/schema/validation/object_must_define_fields.ex +++ b/lib/absinthe/phase/schema/validation/object_must_define_fields.ex @@ -18,6 +18,10 @@ defmodule Absinthe.Phase.Schema.Validation.ObjectMustDefineFields do obj end + defp validate_objects(%Blueprint.Schema.TypeExtensionDefinition{} = node) do + {:halt, node} + end + defp validate_objects(%struct{} = node) when struct in [ Blueprint.Schema.ObjectTypeDefinition, diff --git a/test/absinthe/schema/rule/object_must_define_fields_test.exs b/test/absinthe/schema/rule/object_must_define_fields_test.exs index ca70303fca..5870b47351 100644 --- a/test/absinthe/schema/rule/object_must_define_fields_test.exs +++ b/test/absinthe/schema/rule/object_must_define_fields_test.exs @@ -121,4 +121,24 @@ defmodule Absinthe.Schema.Rule.ObjectMustDefineFieldsTest do Code.eval_string(@schema) end) end + + @schema ~S( + defmodule ExtendObjectSchema do + use Absinthe.Schema + + query do + field :foo, :string + end + + object :bar do + field :baz, :string + end + + extend object :bar do + end + end + ) + test "does not error on empty object extension" do + assert Code.eval_string(@schema) + end end From 7665edc662086740b006a40d2df6b14a5a617c95 Mon Sep 17 00:00:00 2001 From: Maarten van Vliet Date: Fri, 10 Feb 2023 21:51:27 +0100 Subject: [PATCH 071/120] Update changelog --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 45c0aeb462..8f39b1df02 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,9 @@ # Changelog +## Unreleased + +- Bug Fix: [Object type extensions may be empty](https://github.com/absinthe-graphql/absinthe/pull/1228) + ## 1.7.1 - Breaking Bugfix: [Validate repeatable directives on schemas](https://github.com/absinthe-graphql/absinthe/pull/1179) - Breaking Bugfix: [Add "Objects must define fields" schema validation](https://github.com/absinthe-graphql/absinthe/pull/1167) From e0faf8c17c63d5fc3988b0c23c352fed5f552ddc Mon Sep 17 00:00:00 2001 From: Saverio Date: Fri, 17 Feb 2023 14:39:03 +0100 Subject: [PATCH 072/120] Validate input object not being an Enum --- .../phase/document/arguments/parse.ex | 2 +- .../arguments_of_correct_type_test.exs | 42 +++++++++++++++++++ 2 files changed, 43 insertions(+), 1 deletion(-) diff --git a/lib/absinthe/phase/document/arguments/parse.ex b/lib/absinthe/phase/document/arguments/parse.ex index e163d05000..880a432d6e 100644 --- a/lib/absinthe/phase/document/arguments/parse.ex +++ b/lib/absinthe/phase/document/arguments/parse.ex @@ -97,7 +97,7 @@ defmodule Absinthe.Phase.Document.Arguments.Parse do end defp build_value(%{__struct__: struct}, %Type.InputObject{}, _) - when struct in [Input.Boolean, Input.Float, Input.Integer, Input.String] do + when struct in [Input.Boolean, Input.Float, Input.Integer, Input.String, Input.Enum] do {:error, :bad_parse} end diff --git a/test/absinthe/phase/document/validation/arguments_of_correct_type_test.exs b/test/absinthe/phase/document/validation/arguments_of_correct_type_test.exs index 4eb2b69867..64a30cdd80 100644 --- a/test/absinthe/phase/document/validation/arguments_of_correct_type_test.exs +++ b/test/absinthe/phase/document/validation/arguments_of_correct_type_test.exs @@ -873,6 +873,48 @@ defmodule Absinthe.Phase.Document.Validation.ArgumentsOfCorrectTypeTest do end describe "Invalid input object value" do + test "Not an input object, an unquoted string" do + assert_fails_validation( + """ + { + complicatedArgs { + complexArgField(complexArg: SIT) + } + } + """, + [], + [bad_argument("complexArg", "ComplexInput", "SIT", 3, [])] + ) + end + + test "Not an input object, a string" do + assert_fails_validation( + """ + { + complicatedArgs { + complexArgField(complexArg: "SIT") + } + } + """, + [], + [bad_argument("complexArg", "ComplexInput", ~s("SIT"), 3, [])] + ) + end + + test "Not an input object, a number" do + assert_fails_validation( + """ + { + complicatedArgs { + complexArgField(complexArg: 42) + } + } + """, + [], + [bad_argument("complexArg", "ComplexInput", "42", 3, [])] + ) + end + test "Partial object, missing required" do assert_fails_validation( """ From 7a67862ca1f641c7911b72b1a102fd770925985a Mon Sep 17 00:00:00 2001 From: Ben Wilson Date: Fri, 17 Feb 2023 10:09:22 -0500 Subject: [PATCH 073/120] changelog notee --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8f39b1df02..a8ec4dae1a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,7 @@ ## Unreleased - Bug Fix: [Object type extensions may be empty](https://github.com/absinthe-graphql/absinthe/pull/1228) +- Bug Fix: [Validate input object not being an Enum](https://github.com/absinthe-graphql/absinthe/pull/1231) ## 1.7.1 - Breaking Bugfix: [Validate repeatable directives on schemas](https://github.com/absinthe-graphql/absinthe/pull/1179) From 1f836b61128c460126e155c6234ac95240fc613b Mon Sep 17 00:00:00 2001 From: Brian Underwood Date: Wed, 22 Feb 2023 08:54:38 +0100 Subject: [PATCH 074/120] Missing bug fix from CHANGELOG MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit It seems to be in the changes on the [hex.pm hexdiff](https://diff.hex.pm/diff/absinthe/1.7.0..1.7.1) 👍 Thanks for the release! --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index a8ec4dae1a..cc277c9a64 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -24,6 +24,7 @@ - Bug Fix: [Root objects are marked as referenced correctly](https://github.com/absinthe-graphql/absinthe/pull/1186) - Bug Fix: [Prevent DDOS attacks with long queries](https://github.com/absinthe-graphql/absinthe/pull/1220) - Feature: [pipeline_modifier option to Absinthe.run/3](https://github.com/absinthe-graphql/absinthe/pull/1221) +- Bug Fix: [Add end_time_mono to telemetry :stop events](https://github.com/absinthe-graphql/absinthe/pull/1174) ## 1.7.0 From 206fd6fa6c54fe172cd02866295975be1490aebe Mon Sep 17 00:00:00 2001 From: Ellie Poley Date: Mon, 27 Feb 2023 17:52:38 -0600 Subject: [PATCH 075/120] Update test to account for default of :infinity --- test/absinthe/lexer_test.exs | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/test/absinthe/lexer_test.exs b/test/absinthe/lexer_test.exs index 4eec3d7868..592399f6ba 100644 --- a/test/absinthe/lexer_test.exs +++ b/test/absinthe/lexer_test.exs @@ -106,7 +106,13 @@ defmodule Absinthe.LexerTest do end test "document with tokens exceeding limit" do - assert {:error, :exceeded_token_limit} == Absinthe.Lexer.tokenize(too_long_query()) + query = too_long_query() + + assert {:error, :exceeded_token_limit} == + Absinthe.Lexer.tokenize(query, token_limit: 15_000) + + refute {:error, :exceeded_token_limit} == + Absinthe.Lexer.tokenize(query) end defp too_long_query do From 6fd79f3cecf8ac5a93d2cb8d704ae72da36f5f7b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20M=2E=20Pe=C3=B1a?= Date: Wed, 22 Mar 2023 12:22:35 +0100 Subject: [PATCH 076/120] Fix endpoint and docket setup documentation for subscriptions --- guides/subscriptions.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/guides/subscriptions.md b/guides/subscriptions.md index e6b344b879..5c996038ee 100644 --- a/guides/subscriptions.md +++ b/guides/subscriptions.md @@ -50,6 +50,12 @@ options. In your `MyAppWeb.Endpoint` module add: +```elixir +use Absinthe.Phoenix.Endpoint +``` + +Now, you need to configure your socket. I.e. in your `MyAppWeb.UserSocket` module add: + ```elixir use Absinthe.Phoenix.Socket, schema: MyAppWeb.Schema From acf836c2baad7b9c2b6a0a1ec22403af5457abea Mon Sep 17 00:00:00 2001 From: Theo Fiedler Date: Tue, 28 Mar 2023 12:14:17 +0200 Subject: [PATCH 077/120] add previous walk_results/6 as catch all for resolution without path --- lib/absinthe/phase/document/execution/resolution.ex | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/absinthe/phase/document/execution/resolution.ex b/lib/absinthe/phase/document/execution/resolution.ex index d15ddd9cc1..57f3b98b15 100644 --- a/lib/absinthe/phase/document/execution/resolution.ex +++ b/lib/absinthe/phase/document/execution/resolution.ex @@ -118,6 +118,8 @@ defmodule Absinthe.Phase.Document.Execution.Resolution do defp walk_results([], _, _, res = %{path: [_ | sub_path]}, _, acc), do: {:lists.reverse(acc), %{res | path: sub_path}} + defp walk_results([], _, _, res, _, acc), do: {:lists.reverse(acc), res} + defp resolve_fields(parent, res, source, path) do # parent is the parent field, we need to get the return type of that field # that return type could be an interface or union, so let's make it concrete From 4f814d1be17d2ec5f0f0c780f150d513366fe182 Mon Sep 17 00:00:00 2001 From: Ben Wilson Date: Tue, 28 Mar 2023 07:42:16 -0400 Subject: [PATCH 078/120] upgrade to newer ubuntu --- .github/workflows/elixir.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/elixir.yml b/.github/workflows/elixir.yml index 1d74473205..ee3df47593 100644 --- a/.github/workflows/elixir.yml +++ b/.github/workflows/elixir.yml @@ -9,7 +9,7 @@ on: jobs: test: name: Elixir ${{matrix.elixir}} / OTP ${{matrix.otp}} - runs-on: ubuntu-18.04 + runs-on: ubuntu-22.04 strategy: matrix: From 96fd1dd5657bb99e8430f9795fff2e44dd8cfc1c Mon Sep 17 00:00:00 2001 From: Theo Fiedler Date: Tue, 28 Mar 2023 14:08:02 +0200 Subject: [PATCH 079/120] ci: run on ubuntu-20.04 --- .github/workflows/elixir.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/elixir.yml b/.github/workflows/elixir.yml index 1d74473205..7eefc16d13 100644 --- a/.github/workflows/elixir.yml +++ b/.github/workflows/elixir.yml @@ -9,7 +9,7 @@ on: jobs: test: name: Elixir ${{matrix.elixir}} / OTP ${{matrix.otp}} - runs-on: ubuntu-18.04 + runs-on: ubuntu-20.04 strategy: matrix: From b425deba3fc90c11aeae7786a32d5206a771e36d Mon Sep 17 00:00:00 2001 From: Maxime Buffa Date: Wed, 29 Mar 2023 17:41:59 +0200 Subject: [PATCH 080/120] Fix Absinthe.Subscription parameter in Subscriptions guide --- guides/subscriptions.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/guides/subscriptions.md b/guides/subscriptions.md index 5c996038ee..11030ed282 100644 --- a/guides/subscriptions.md +++ b/guides/subscriptions.md @@ -36,7 +36,7 @@ line: {Phoenix.PubSub, name: :my_pubsub}, # Start the endpoint when the application starts MyAppWeb.Endpoint, - {Absinthe.Subscription, pubsub: MyAppWeb.Endpoint} + {Absinthe.Subscription, MyAppWeb.Endpoint} ] # See https://hexdocs.pm/elixir/Supervisor.html From 628ea46f98a9da8f6c09f1091c15cc187643c8c6 Mon Sep 17 00:00:00 2001 From: Maxime Buffa Date: Wed, 29 Mar 2023 17:51:56 +0200 Subject: [PATCH 081/120] Do the same for the tutorial --- guides/tutorial/subscriptions.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/guides/tutorial/subscriptions.md b/guides/tutorial/subscriptions.md index 1000a2007b..f2a5dcfddc 100644 --- a/guides/tutorial/subscriptions.md +++ b/guides/tutorial/subscriptions.md @@ -33,7 +33,7 @@ In `lib/blog/application.ex`: children = [ # other children ... {BlogWeb.Endpoint, []}, # this line should already exist - {Absinthe.Subscription, pubsub: BlogWeb.Endpoint}, # add this line + {Absinthe.Subscription, BlogWeb.Endpoint}, # add this line # other children ... ] ``` From 46da214aa123e7dc0977b78ed3ba0acace4ba7e6 Mon Sep 17 00:00:00 2001 From: Doruk Gurleyen Date: Fri, 3 Feb 2023 17:11:25 -0600 Subject: [PATCH 082/120] Allow maps as arguments in macros --- lib/absinthe/blueprint/input.ex | 5 +++++ .../notation/experimental/macro_extensions_test.exs | 13 ++++++++++++- 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/lib/absinthe/blueprint/input.ex b/lib/absinthe/blueprint/input.ex index 8ae8384dbe..75be3d576f 100644 --- a/lib/absinthe/blueprint/input.ex +++ b/lib/absinthe/blueprint/input.ex @@ -69,6 +69,11 @@ defmodule Absinthe.Blueprint.Input do } end + # Handle maps in AST format if they are not unquoted in macros + def parse({:%{}, _, map_key_values}) when is_list(map_key_values) do + map_key_values |> Enum.into(%{}) |> parse() + end + def parse(value) when is_map(value) do %Input.Object{ fields: diff --git a/test/absinthe/schema/notation/experimental/macro_extensions_test.exs b/test/absinthe/schema/notation/experimental/macro_extensions_test.exs index 39fdaa21af..917b2a168f 100644 --- a/test/absinthe/schema/notation/experimental/macro_extensions_test.exs +++ b/test/absinthe/schema/notation/experimental/macro_extensions_test.exs @@ -8,8 +8,13 @@ defmodule Absinthe.Schema.Notation.Experimental.MacroExtensionsTest do defmodule WithFeatureDirective do use Absinthe.Schema.Prototype + input_object :related_feature do + field :name, :string + end + directive :feature do arg :name, :string + arg :related_features, list_of(:related_feature) on [:scalar, :schema] expand(fn _args, node -> @@ -28,7 +33,7 @@ defmodule Absinthe.Schema.Notation.Experimental.MacroExtensionsTest do end extend schema do - directive :feature + directive :feature, related_features: [%{"name" => "another_feature"}] end object :person do @@ -237,6 +242,12 @@ defmodule Absinthe.Schema.Notation.Experimental.MacroExtensionsTest do assert [:valued_entity] = object.interfaces end + test "can use map in arguments" do + sdl = Absinthe.Schema.to_sdl(ExtendedSchema) + + assert sdl =~ "schema @feature(related_features: [{name: \"another_feature\"}])" + end + test "raises when definition types do not match" do schema = """ defmodule KeywordExtend do From b326789e104cd558bbee45409fc23c1a540ad70d Mon Sep 17 00:00:00 2001 From: Doruk Gurleyen Date: Thu, 30 Mar 2023 17:22:48 -0400 Subject: [PATCH 083/120] Handle quoted map argument handling in Notation instead of Input --- lib/absinthe/blueprint/input.ex | 5 ----- lib/absinthe/schema/notation.ex | 19 ++++++++++++++++++- 2 files changed, 18 insertions(+), 6 deletions(-) diff --git a/lib/absinthe/blueprint/input.ex b/lib/absinthe/blueprint/input.ex index 75be3d576f..8ae8384dbe 100644 --- a/lib/absinthe/blueprint/input.ex +++ b/lib/absinthe/blueprint/input.ex @@ -69,11 +69,6 @@ defmodule Absinthe.Blueprint.Input do } end - # Handle maps in AST format if they are not unquoted in macros - def parse({:%{}, _, map_key_values}) when is_list(map_key_values) do - map_key_values |> Enum.into(%{}) |> parse() - end - def parse(value) when is_map(value) do %Input.Object{ fields: diff --git a/lib/absinthe/schema/notation.ex b/lib/absinthe/schema/notation.ex index ab23d37f36..db2f101bc0 100644 --- a/lib/absinthe/schema/notation.ex +++ b/lib/absinthe/schema/notation.ex @@ -2404,7 +2404,8 @@ defmodule Absinthe.Schema.Notation do end defp expand_ast(ast, env) do - Macro.prewalk(ast, fn + ast + |> Macro.prewalk(fn # We don't want to expand `@bla` into `Module.get_attribute(module, @bla)` because this # function call will fail if the module is already compiled. Remember that the ast gets put # into a generated `__absinthe_blueprint__` function which is called at "__after_compile__" @@ -2423,6 +2424,22 @@ defmodule Absinthe.Schema.Notation do node -> node end) + |> expand_ast_map() + end + + # Handle maps in AST format if they are not escaped in macros + defp expand_ast_map({:%{}, _, map_key_values} = _node) when is_list(map_key_values) do + map_key_values + |> Enum.map(fn {key, val} -> {key, expand_ast_map(val)} end) + |> Enum.into(%{}) + end + + defp expand_ast_map(node) when is_list(node) do + Enum.map(node, &expand_ast_map/1) + end + + defp expand_ast_map(node) do + node end @doc false From c982582910dc02bbf6057e63b8ffa1cefa88eb2f Mon Sep 17 00:00:00 2001 From: "mae.kasza" <26093674+MaeIsBad@users.noreply.github.com> Date: Thu, 13 Apr 2023 12:56:26 +0200 Subject: [PATCH 084/120] Deduplicate directives when building schema When importing directives from the prototype some of them were duplicated, which caused multiple functions with the same clause to be generated. --- lib/absinthe/phase/schema/build.ex | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/lib/absinthe/phase/schema/build.ex b/lib/absinthe/phase/schema/build.ex index a5b5f895aa..2e1ad552c9 100644 --- a/lib/absinthe/phase/schema/build.ex +++ b/lib/absinthe/phase/schema/build.ex @@ -5,12 +5,15 @@ defmodule Absinthe.Phase.Schema.Build do %{schema_definitions: [schema]} = blueprint types = build_types(blueprint) - directive_artifacts = build_directives(blueprint) + + directive_artifacts = + (schema.directive_artifacts ++ build_directives(blueprint)) + |> Enum.uniq_by(fn v -> v.identifier end) schema = %{ schema | type_artifacts: types, - directive_artifacts: schema.directive_artifacts ++ directive_artifacts + directive_artifacts: directive_artifacts } blueprint = %{blueprint | schema_definitions: [schema]} From 520b1304972ff477b09d79198c20ee2fd63e2e05 Mon Sep 17 00:00:00 2001 From: "mae.kasza" <26093674+MaeIsBad@users.noreply.github.com> Date: Thu, 13 Apr 2023 13:04:11 +0200 Subject: [PATCH 085/120] Update CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index cc277c9a64..8eee4240cc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,7 @@ - Bug Fix: [Object type extensions may be empty](https://github.com/absinthe-graphql/absinthe/pull/1228) - Bug Fix: [Validate input object not being an Enum](https://github.com/absinthe-graphql/absinthe/pull/1231) +- Bug Fix: [Deduplicate directives when building schema](https://github.com/absinthe-graphql/absinthe/pull/1242) ## 1.7.1 - Breaking Bugfix: [Validate repeatable directives on schemas](https://github.com/absinthe-graphql/absinthe/pull/1179) From 17f78c94e2b1a16a58fc944451231f47622b7095 Mon Sep 17 00:00:00 2001 From: "mae.kasza" <26093674+MaeIsBad@users.noreply.github.com> Date: Thu, 13 Apr 2023 14:32:09 +0200 Subject: [PATCH 086/120] Revert "Deduplicate directives when building schema" This reverts commit c982582910dc02bbf6057e63b8ffa1cefa88eb2f. --- lib/absinthe/phase/schema/build.ex | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/lib/absinthe/phase/schema/build.ex b/lib/absinthe/phase/schema/build.ex index 2e1ad552c9..a5b5f895aa 100644 --- a/lib/absinthe/phase/schema/build.ex +++ b/lib/absinthe/phase/schema/build.ex @@ -5,15 +5,12 @@ defmodule Absinthe.Phase.Schema.Build do %{schema_definitions: [schema]} = blueprint types = build_types(blueprint) - - directive_artifacts = - (schema.directive_artifacts ++ build_directives(blueprint)) - |> Enum.uniq_by(fn v -> v.identifier end) + directive_artifacts = build_directives(blueprint) schema = %{ schema | type_artifacts: types, - directive_artifacts: directive_artifacts + directive_artifacts: schema.directive_artifacts ++ directive_artifacts } blueprint = %{blueprint | schema_definitions: [schema]} From 92ae35254717cefbe68e98b78a9c1eaa0905bac8 Mon Sep 17 00:00:00 2001 From: "mae.kasza" <26093674+MaeIsBad@users.noreply.github.com> Date: Thu, 13 Apr 2023 14:35:18 +0200 Subject: [PATCH 087/120] Don't import default directives into the prototype schema They are of no use there as include/skip only work at the runtime level of a document's execution and the default directives are already included when generating a schema. Importing the same directives multiple times caused issues due to absinthe generating multiple functions with the same clause. Co-authored-by: Maarten van Vliet Co-authored-by: Ben Wilson --- lib/absinthe/schema/prototype/notation.ex | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/absinthe/schema/prototype/notation.ex b/lib/absinthe/schema/prototype/notation.ex index 2818d28396..e06dd0ced6 100644 --- a/lib/absinthe/schema/prototype/notation.ex +++ b/lib/absinthe/schema/prototype/notation.ex @@ -38,6 +38,7 @@ defmodule Absinthe.Schema.Prototype.Notation do pipeline |> Absinthe.Pipeline.without(Absinthe.Phase.Schema.Validation.QueryTypeMustBeObject) |> Absinthe.Pipeline.without(Absinthe.Phase.Schema.ImportPrototypeDirectives) + |> Absinthe.Pipeline.without(Absinthe.Phase.Schema.DirectiveImports) |> Absinthe.Pipeline.replace( Absinthe.Phase.Schema.TypeExtensionImports, {Absinthe.Phase.Schema.TypeExtensionImports, []} From 9e2398de63537586209de6270fda2fe098c66e62 Mon Sep 17 00:00:00 2001 From: Ben Wilson Date: Sat, 13 May 2023 12:23:38 -0400 Subject: [PATCH 088/120] typo fix --- test/absinthe/schema/manipulation_test.exs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/absinthe/schema/manipulation_test.exs b/test/absinthe/schema/manipulation_test.exs index 5cfd5feee3..ce1066d17f 100644 --- a/test/absinthe/schema/manipulation_test.exs +++ b/test/absinthe/schema/manipulation_test.exs @@ -25,7 +25,7 @@ defmodule Absinthe.Schema.ManipulationTest do description "Simple Helper Object used to define blueprint fields" field :simple_string, :string do - description "customer introspection field" + description "custom introspection field" resolve fn _, %{schema: schema} -> {:ok, "This is a new introspection type on #{inspect(schema)}"} From c6d94b0e3fd7d25abe41a8940c3bba9494997bc1 Mon Sep 17 00:00:00 2001 From: Ben Wilson Date: Sun, 14 May 2023 07:17:44 -0400 Subject: [PATCH 089/120] cleanup some warnings --- test/absinthe/schema/rule/unique_field_names_test.exs | 4 ++-- test/absinthe/type/interface_test.exs | 4 ++-- test/mix/tasks/absinthe.schema.json_test.exs | 7 ------- test/mix/tasks/absinthe.schema.sdl_test.exs | 7 ------- 4 files changed, 4 insertions(+), 18 deletions(-) diff --git a/test/absinthe/schema/rule/unique_field_names_test.exs b/test/absinthe/schema/rule/unique_field_names_test.exs index a62329e53f..d652b067fd 100644 --- a/test/absinthe/schema/rule/unique_field_names_test.exs +++ b/test/absinthe/schema/rule/unique_field_names_test.exs @@ -2,7 +2,7 @@ defmodule Absinthe.Schema.Rule.UniqueFieldNamesTest do use Absinthe.Case, async: true @duplicate_object_fields_macro ~S( - defmodule DuplicateObjectFields do + defmodule DuplicateObjectFieldsMacro do use Absinthe.Schema query do @@ -16,7 +16,7 @@ defmodule Absinthe.Schema.Rule.UniqueFieldNamesTest do ) @duplicate_object_fields_sdl ~S( - defmodule DuplicateObjectFields do + defmodule DuplicateObjectFieldsSDL do use Absinthe.Schema query do diff --git a/test/absinthe/type/interface_test.exs b/test/absinthe/type/interface_test.exs index 43393fb1ce..857ef6c703 100644 --- a/test/absinthe/type/interface_test.exs +++ b/test/absinthe/type/interface_test.exs @@ -358,7 +358,7 @@ defmodule Absinthe.Type.InterfaceTest do description "A collection" field :name, non_null(:string) - resolve_type fn value, %{context: %{auth: is_auth}} -> + resolve_type fn _value, %{context: %{auth: is_auth}} -> if is_auth, do: :private_collection, else: :public_collection end end @@ -378,7 +378,7 @@ defmodule Absinthe.Type.InterfaceTest do description "An item" field :title, non_null(:string) - resolve_type fn value, %{path: path} -> + resolve_type fn _value, %{path: path} -> assert [ idx, %{name: "items", parent_type: %{identifier: parent_id}}, diff --git a/test/mix/tasks/absinthe.schema.json_test.exs b/test/mix/tasks/absinthe.schema.json_test.exs index acf0ea6110..7667e2a5b3 100644 --- a/test/mix/tasks/absinthe.schema.json_test.exs +++ b/test/mix/tasks/absinthe.schema.json_test.exs @@ -125,11 +125,4 @@ defmodule Mix.Tasks.Absinthe.Schema.JsonTest do assert File.exists?(path) end end - - defp tmp_dir_fallback(_) do - path = Path.join("tmp", "#{__MODULE__}") - File.mkdir_p!(path) - on_exit(fn -> File.rm_rf!(path) end) - [tmp_dir: path] - end end diff --git a/test/mix/tasks/absinthe.schema.sdl_test.exs b/test/mix/tasks/absinthe.schema.sdl_test.exs index 8da3afc4ac..db7cfc30c3 100644 --- a/test/mix/tasks/absinthe.schema.sdl_test.exs +++ b/test/mix/tasks/absinthe.schema.sdl_test.exs @@ -204,11 +204,4 @@ defmodule Mix.Tasks.Absinthe.Schema.SdlTest do assert File.exists?(path) end end - - defp tmp_dir_fallback(_) do - path = Path.join("tmp", "#{__MODULE__}") - File.mkdir_p!(path) - on_exit(fn -> File.rm_rf!(path) end) - [tmp_dir: path] - end end From 05ca9f278afb61b50f172dccc241ee1bcd4e32d0 Mon Sep 17 00:00:00 2001 From: Ben Wilson Date: Sun, 14 May 2023 07:24:34 -0400 Subject: [PATCH 090/120] typo fix --- test/absinthe/execution/subscription_test.exs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/absinthe/execution/subscription_test.exs b/test/absinthe/execution/subscription_test.exs index d7e2bdb00e..50272b5c7b 100644 --- a/test/absinthe/execution/subscription_test.exs +++ b/test/absinthe/execution/subscription_test.exs @@ -3,7 +3,7 @@ defmodule Absinthe.Execution.SubscriptionTest do import ExUnit.CaptureLog - defmodule ResultPase do + defmodule ResultPhase do @moduledoc false alias Absinthe.Blueprint @@ -206,7 +206,7 @@ defmodule Absinthe.Execution.SubscriptionTest do Schema, variables: %{"clientId" => client_id}, context: %{pubsub: PubSub}, - result_phase: ResultPase + result_phase: ResultPhase ) Absinthe.Subscription.publish(PubSub, %{foo: "bar"}, thing: client_id) From 11b56c31afd7f2b31a9073a628b0c1d3beae04e0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Valim?= Date: Wed, 17 May 2023 16:25:46 +0200 Subject: [PATCH 091/120] Do not unquote AST twice Instead of generating def __absinthe_type__(:foo_bar), do: unquote(ast) def __absinthe_type__("FooBar"), do: unquote(ast) We generate: def __absinthe_type__(:foo_bar), do: unquote(ast) def __absinthe_type__("FooBar"), do: __absinthe_type__(:foo_bar) This considerably reduces the size of the generated code. Compilation times went from 1.8s to 1.2s in a sample schema. --- lib/absinthe/phase/schema/compile.ex | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/absinthe/phase/schema/compile.ex b/lib/absinthe/phase/schema/compile.ex index 78a72416d9..5046119371 100644 --- a/lib/absinthe/phase/schema/compile.ex +++ b/lib/absinthe/phase/schema/compile.ex @@ -103,7 +103,7 @@ defmodule Absinthe.Phase.Schema.Compile do end def __absinthe_type__(unquote(type.name)) do - unquote(ast) + __absinthe_type__(unquote(type.identifier)) end end end @@ -126,7 +126,7 @@ defmodule Absinthe.Phase.Schema.Compile do end def __absinthe_directive__(unquote(type.name)) do - unquote(ast) + __absinthe_directive__(unquote(type.identifier)) end end end From 665a9ec43a7875160c572eaecf6fc4660a55281c Mon Sep 17 00:00:00 2001 From: Noel Han Date: Wed, 7 Jun 2023 09:21:07 +0900 Subject: [PATCH 092/120] Add arg into resolver doc --- lib/absinthe/schema/notation.ex | 3 +++ 1 file changed, 3 insertions(+) diff --git a/lib/absinthe/schema/notation.ex b/lib/absinthe/schema/notation.ex index 3d5b2b08a7..0bd8fe1ec9 100644 --- a/lib/absinthe/schema/notation.ex +++ b/lib/absinthe/schema/notation.ex @@ -640,6 +640,7 @@ defmodule Absinthe.Schema.Notation do ``` query do field :person, :person do + arg :id, non_null(:id) resolve &Person.resolve/2 end end @@ -648,6 +649,7 @@ defmodule Absinthe.Schema.Notation do ``` query do field :person, :person do + arg :id, non_null(:id) resolve fn %{id: id}, _ -> {:ok, Person.find(id)} end @@ -658,6 +660,7 @@ defmodule Absinthe.Schema.Notation do ``` query do field :person, :person do + arg :id, non_null(:id) resolve lookup(:person) end end From c49f2ed6b2fc410793e3bf3642fc269a0e44ba0f Mon Sep 17 00:00:00 2001 From: "Paulo F. Oliveira" Date: Fri, 16 Jun 2023 22:21:50 +0100 Subject: [PATCH 093/120] Add dialyxir to CI (and stop analysing dependencies) --- .github/workflows/elixir.yml | 14 ++++++++++++++ mix.exs | 1 + 2 files changed, 15 insertions(+) diff --git a/.github/workflows/elixir.yml b/.github/workflows/elixir.yml index 7eefc16d13..a0d6ccd9e6 100644 --- a/.github/workflows/elixir.yml +++ b/.github/workflows/elixir.yml @@ -64,3 +64,17 @@ jobs: mix test env: SCHEMA_PROVIDER: persistent_term + + - name: Cache/uncache PLTs + uses: actions/cache@v3 + with: + path: | + priv/plts + key: "${{ runner.os }}-\ + erlang-${{ matrix.otp }}-\ + elixir-${{ matrix.elixir }}-\ + ${{ hashFiles('mix.lock') }}" + + - name: Run Dialyzer + run: mix dialyzer + diff --git a/mix.exs b/mix.exs index 7b2361679f..951c45b333 100644 --- a/mix.exs +++ b/mix.exs @@ -30,6 +30,7 @@ defmodule Absinthe.Mixfile do ], deps: deps(), dialyzer: [ + plt_add_deps: :apps_direct, plt_core_path: "priv/plts", plt_add_apps: [:mix, :dataloader, :decimal, :ex_unit] ] From aed31be521c5f5527f6aff2a4f69593209db9602 Mon Sep 17 00:00:00 2001 From: "Paulo F. Oliveira" Date: Fri, 16 Jun 2023 22:22:15 +0100 Subject: [PATCH 094/120] Bump mix_test_watch to prevent compilation warnings --- mix.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mix.lock b/mix.lock index a956f2cf5c..cb4b6072eb 100644 --- a/mix.lock +++ b/mix.lock @@ -12,7 +12,7 @@ "makeup_elixir": {:hex, :makeup_elixir, "0.16.0", "f8c570a0d33f8039513fbccaf7108c5d750f47d8defd44088371191b76492b0b", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}, {:nimble_parsec, "~> 1.2.3", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "28b2cbdc13960a46ae9a8858c4bebdec3c9a6d7b4b9e7f4ed1502f8159f338e7"}, "makeup_erlang": {:hex, :makeup_erlang, "0.1.1", "3fcb7f09eb9d98dc4d208f49cc955a34218fc41ff6b84df7c75b3e6e533cc65f", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}], "hexpm", "174d0809e98a4ef0b3309256cbf97101c6ec01c4ab0b23e926a9e17df2077cbb"}, "makeup_graphql": {:hex, :makeup_graphql, "0.1.2", "81e2939aab6d2b81d39ee5d9e13fae02599e9ca6e1152e0eeed737a98a5f96aa", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}, {:nimble_parsec, "~> 1.1", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "3390ab04ba388d52a94bbe64ef62aa4d7923ceaffac43ec948f58f631440e8fb"}, - "mix_test_watch": {:hex, :mix_test_watch, "1.0.2", "34900184cbbbc6b6ed616ed3a8ea9b791f9fd2088419352a6d3200525637f785", [:mix], [{:file_system, "~> 0.2.1 or ~> 0.3", [hex: :file_system, repo: "hexpm", optional: false]}], "hexpm", "47ac558d8b06f684773972c6d04fcc15590abdb97aeb7666da19fcbfdc441a07"}, + "mix_test_watch": {:hex, :mix_test_watch, "1.1.0", "330bb91c8ed271fe408c42d07e0773340a7938d8a0d281d57a14243eae9dc8c3", [:mix], [{:file_system, "~> 0.2.1 or ~> 0.3", [hex: :file_system, repo: "hexpm", optional: false]}], "hexpm", "52b6b1c476cbb70fd899ca5394506482f12e5f6b0d6acff9df95c7f1e0812ec3"}, "nimble_parsec": {:hex, :nimble_parsec, "1.2.3", "244836e6e3f1200c7f30cb56733fd808744eca61fd182f731eac4af635cc6d0b", [:mix], [], "hexpm", "c8d789e39b9131acf7b99291e93dae60ab48ef14a7ee9d58c6964f59efb570b0"}, "opentelemetry_api": {:hex, :opentelemetry_api, "1.2.0", "454a35655b4c1924405ef1f3587f2c6f141bf73366b2c5e8a38dcc619b53eaa0", [:mix, :rebar3], [], "hexpm", "9e677c68243de0f70538798072e66e1fb1d4a2ca8888a6eb493c0a41e5480c35"}, "opentelemetry_process_propagator": {:hex, :opentelemetry_process_propagator, "0.2.1", "20ac37648faf7175cade16fda8d58e6f1ff1b7f2a50a8ef9d70a032c41aba315", [:mix, :rebar3], [{:opentelemetry_api, "~> 1.0", [hex: :opentelemetry_api, repo: "hexpm", optional: false]}], "hexpm", "f317237e39636d4f6140afa5d419e85ed3dc9e9a57072e7cd442df42af7b8aac"}, From 6795e8ad1d5d5ce01028874164fd19ba5591473c Mon Sep 17 00:00:00 2001 From: "Paulo F. Oliveira" Date: Fri, 16 Jun 2023 22:22:59 +0100 Subject: [PATCH 095/120] Bump benchee to prevent compilation warnings --- mix.lock | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/mix.lock b/mix.lock index cb4b6072eb..a27456d867 100644 --- a/mix.lock +++ b/mix.lock @@ -1,5 +1,5 @@ %{ - "benchee": {:hex, :benchee, "1.0.1", "66b211f9bfd84bd97e6d1beaddf8fc2312aaabe192f776e8931cb0c16f53a521", [:mix], [{:deep_merge, "~> 1.0", [hex: :deep_merge, repo: "hexpm", optional: false]}], "hexpm", "3ad58ae787e9c7c94dd7ceda3b587ec2c64604563e049b2a0e8baafae832addb"}, + "benchee": {:hex, :benchee, "1.1.0", "f3a43817209a92a1fade36ef36b86e1052627fd8934a8b937ac9ab3a76c43062", [:mix], [{:deep_merge, "~> 1.0", [hex: :deep_merge, repo: "hexpm", optional: false]}, {:statistex, "~> 1.0", [hex: :statistex, repo: "hexpm", optional: false]}], "hexpm", "7da57d545003165a012b587077f6ba90b89210fd88074ce3c60ce239eb5e6d93"}, "dataloader": {:hex, :dataloader, "1.0.8", "114294362db98a613f231589246aa5b0ce847412e8e75c4c94f31f204d272cbf", [:mix], [{:ecto, ">= 3.4.3 and < 4.0.0", [hex: :ecto, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "eaf3c2aa2bc9dbd2f1e960561d616b7f593396c4754185b75904f6d66c82a667"}, "decimal": {:hex, :decimal, "2.0.0", "a78296e617b0f5dd4c6caf57c714431347912ffb1d0842e998e9792b5642d697", [:mix], [], "hexpm", "34666e9c55dea81013e77d9d87370fe6cb6291d1ef32f46a1600230b1d44f577"}, "deep_merge": {:hex, :deep_merge, "1.0.0", "b4aa1a0d1acac393bdf38b2291af38cb1d4a52806cf7a4906f718e1feb5ee961", [:mix], [], "hexpm", "ce708e5f094b9cd4e8f2be4f00d2f4250c4095be93f8cd6d018c753894885430"}, @@ -16,5 +16,6 @@ "nimble_parsec": {:hex, :nimble_parsec, "1.2.3", "244836e6e3f1200c7f30cb56733fd808744eca61fd182f731eac4af635cc6d0b", [:mix], [], "hexpm", "c8d789e39b9131acf7b99291e93dae60ab48ef14a7ee9d58c6964f59efb570b0"}, "opentelemetry_api": {:hex, :opentelemetry_api, "1.2.0", "454a35655b4c1924405ef1f3587f2c6f141bf73366b2c5e8a38dcc619b53eaa0", [:mix, :rebar3], [], "hexpm", "9e677c68243de0f70538798072e66e1fb1d4a2ca8888a6eb493c0a41e5480c35"}, "opentelemetry_process_propagator": {:hex, :opentelemetry_process_propagator, "0.2.1", "20ac37648faf7175cade16fda8d58e6f1ff1b7f2a50a8ef9d70a032c41aba315", [:mix, :rebar3], [{:opentelemetry_api, "~> 1.0", [hex: :opentelemetry_api, repo: "hexpm", optional: false]}], "hexpm", "f317237e39636d4f6140afa5d419e85ed3dc9e9a57072e7cd442df42af7b8aac"}, + "statistex": {:hex, :statistex, "1.0.0", "f3dc93f3c0c6c92e5f291704cf62b99b553253d7969e9a5fa713e5481cd858a5", [:mix], [], "hexpm", "ff9d8bee7035028ab4742ff52fc80a2aa35cece833cf5319009b52f1b5a86c27"}, "telemetry": {:hex, :telemetry, "0.4.3", "a06428a514bdbc63293cd9a6263aad00ddeb66f608163bdec7c8995784080818", [:rebar3], [], "hexpm", "eb72b8365ffda5bed68a620d1da88525e326cb82a75ee61354fc24b844768041"}, } From 8ecf60b3effa40ec4fb594d7ed67b244faa54aef Mon Sep 17 00:00:00 2001 From: "Paulo F. Oliveira" Date: Fri, 16 Jun 2023 22:25:34 +0100 Subject: [PATCH 096/120] Update spec to prevent consumer errors on Dialyzer'ing --- lib/absinthe/pipeline.ex | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/lib/absinthe/pipeline.ex b/lib/absinthe/pipeline.ex index 89a22fc59b..16c7d1c2ed 100644 --- a/lib/absinthe/pipeline.ex +++ b/lib/absinthe/pipeline.ex @@ -20,7 +20,9 @@ defmodule Absinthe.Pipeline do @type t :: [phase_config_t | [phase_config_t]] - @spec run(data_t, t) :: {:ok, data_t, [Phase.t()]} | {:error, String.t(), [Phase.t()]} + @spec run(data_t, t) :: + {:ok, data_t, [Phase.t()]} + | {:error, String.t() | {:http_method, String.t()}, [Phase.t()]} def run(input, pipeline) do pipeline |> List.flatten() @@ -392,7 +394,8 @@ defmodule Absinthe.Pipeline do end @spec run_phase(t, data_t, [Phase.t()]) :: - {:ok, data_t, [Phase.t()]} | {:error, String.t(), [Phase.t()]} + {:ok, data_t, [Phase.t()]} + | {:error, String.t() | {:http_method, String.t()}, [Phase.t()]} def run_phase(pipeline, input, done \\ []) def run_phase([], input, done) do From dde99094f0fa1473ef5f783d9ca80f0d4a37417e Mon Sep 17 00:00:00 2001 From: "Paulo F. Oliveira" Date: Fri, 16 Jun 2023 23:14:05 +0100 Subject: [PATCH 097/120] Fix all Dialyzer -issued warnings --- .dialyzer_ignore.exs | 5 ++++- lib/absinthe/blueprint/source_location.ex | 2 +- lib/absinthe/lexer.ex | 20 +++++++++++++------- lib/absinthe/phase/parse.ex | 2 +- 4 files changed, 19 insertions(+), 10 deletions(-) diff --git a/.dialyzer_ignore.exs b/.dialyzer_ignore.exs index a32ac33b23..2a85af071a 100644 --- a/.dialyzer_ignore.exs +++ b/.dialyzer_ignore.exs @@ -1,4 +1,7 @@ [ {":0:unknown_function Function :persistent_term.get/1 does not exist."}, - {":0:unknown_function Function :persistent_term.put/2 does not exist."} + {":0:unknown_function Function :persistent_term.put/2 does not exist."}, + {"lib/absinthe/middleware/async.ex", :unknown_function, 117}, + {"lib/absinthe/middleware/batch.ex", :unknown_function, 213}, + {"lib/absinthe/utils/render.ex", :improper_list_constr, 51} ] diff --git a/lib/absinthe/blueprint/source_location.ex b/lib/absinthe/blueprint/source_location.ex index 58aad966bb..5b08550899 100644 --- a/lib/absinthe/blueprint/source_location.ex +++ b/lib/absinthe/blueprint/source_location.ex @@ -23,7 +23,7 @@ defmodule Absinthe.Blueprint.SourceLocation do @doc """ Generate a `SourceLocation.t()` given line and column numbers """ - @spec at(line :: pos_integer, column :: pos_integer) :: t + @spec at(line :: pos_integer, column :: non_neg_integer) :: t def at(line, column) do %__MODULE__{line: line, column: column} end diff --git a/lib/absinthe/lexer.ex b/lib/absinthe/lexer.ex index c637ff352e..0af0d1acbc 100644 --- a/lib/absinthe/lexer.ex +++ b/lib/absinthe/lexer.ex @@ -8,6 +8,8 @@ defmodule Absinthe.Lexer do @space 0x0020 @unicode_bom 0xFEFF + @stopped_at_token_limit ":stopped_at_token_limit" + # SourceCharacter :: /[\u0009\u000A\u000D\u0020-\uFFFF]/ any_unicode = utf8_char([]) @@ -228,14 +230,16 @@ defmodule Absinthe.Lexer do end @spec tokenize(binary(), Keyword.t()) :: - {:ok, [any()]} | {:error, binary(), {integer(), non_neg_integer()}} + {:ok, [any()]} + | {:error, binary(), {integer(), non_neg_integer()}} + | {:error, :exceeded_token_limit} def tokenize(input, options \\ []) do lines = String.split(input, ~r/\r?\n/) tokenize_opts = [context: %{token_limit: Keyword.get(options, :token_limit, :infinity)}] case do_tokenize(input, tokenize_opts) do - {:error, :stopped_at_token_limit, _, _, _, _} -> + {:error, @stopped_at_token_limit, _, _, _, _} -> {:error, :exceeded_token_limit} {:ok, tokens, "", _, _, _} -> @@ -341,6 +345,8 @@ defmodule Absinthe.Lexer do @spec do_tokenize(binary()) :: {:ok, [any()], binary(), map(), {pos_integer(), pos_integer()}, pos_integer()} + | {:error, String.t(), String.t(), map(), {non_neg_integer(), non_neg_integer()}, + non_neg_integer()} defparsec( :do_tokenize, repeat( @@ -399,7 +405,7 @@ defmodule Absinthe.Lexer do _ ) when count >= limit do - {:error, :stopped_at_token_limit} + {:error, @stopped_at_token_limit} end defp boolean_value_or_name_or_reserved_word(rest, chars, context, loc, byte_offset) do @@ -425,7 +431,7 @@ defmodule Absinthe.Lexer do defp labeled_token(_, _, %{token_count: count, token_limit: limit} = _context, _, _, _) when count >= limit, - do: {:error, :stopped_at_token_limit} + do: {:error, @stopped_at_token_limit} defp labeled_token(rest, chars, context, loc, byte_offset, token_name) do context = Map.update(context, :token_count, 1, &(&1 + 1)) @@ -443,7 +449,7 @@ defmodule Absinthe.Lexer do defp block_string_value_token(_, _, %{token_count: count, token_limit: limit} = _context, _, _) when count >= limit, - do: {:error, :stopped_at_token_limit} + do: {:error, @stopped_at_token_limit} defp block_string_value_token(rest, chars, context, _loc, _byte_offset) do context = Map.update(context, :token_count, 1, &(&1 + 1)) @@ -455,7 +461,7 @@ defmodule Absinthe.Lexer do defp string_value_token(_, _, %{token_count: count, token_limit: limit} = _context, _, _) when count >= limit, - do: {:error, :stopped_at_token_limit} + do: {:error, @stopped_at_token_limit} defp string_value_token(rest, chars, context, _loc, _byte_offset) do context = Map.update(context, :token_count, 1, &(&1 + 1)) @@ -465,7 +471,7 @@ defmodule Absinthe.Lexer do defp atom_token(_, _, %{token_count: count, token_limit: limit} = _context, _, _) when count >= limit do - {:error, :stopped_at_token_limit} + {:error, @stopped_at_token_limit} end defp atom_token(rest, chars, context, loc, byte_offset) do diff --git a/lib/absinthe/phase/parse.ex b/lib/absinthe/phase/parse.ex index 680671a13e..d1c0e25a42 100644 --- a/lib/absinthe/phase/parse.ex +++ b/lib/absinthe/phase/parse.ex @@ -59,7 +59,7 @@ defmodule Absinthe.Phase.Parse do # This is because Dialyzer is telling us tokenizing can never fail, # but we know it's possible. @dialyzer {:no_match, parse: 2} - @spec parse(binary | Language.Source.t(), Map.t()) :: + @spec parse(binary | Language.Source.t(), Keyword.t()) :: {:ok, Language.Document.t()} | {:error, tuple} defp parse(input, options) when is_binary(input) do parse(%Language.Source{body: input}, options) From e236f9656ea1f2c9927c3092fc8ff4ad049c6e13 Mon Sep 17 00:00:00 2001 From: "Paulo F. Oliveira" Date: Fri, 16 Jun 2023 23:27:28 +0100 Subject: [PATCH 098/120] Tweak config. as per recent dialyxir documentation This way it works both locally and in CI (GitHub) --- mix.exs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mix.exs b/mix.exs index 951c45b333..8ea65fcc76 100644 --- a/mix.exs +++ b/mix.exs @@ -31,7 +31,7 @@ defmodule Absinthe.Mixfile do deps: deps(), dialyzer: [ plt_add_deps: :apps_direct, - plt_core_path: "priv/plts", + plt_file: {:no_warn, "priv/plts/project.plt"}, plt_add_apps: [:mix, :dataloader, :decimal, :ex_unit] ] ] From 8f737bf853366b88a16c50dece68ddbb8db4393f Mon Sep 17 00:00:00 2001 From: "Paulo F. Oliveira" Date: Fri, 16 Jun 2023 23:51:27 +0100 Subject: [PATCH 099/120] Act on CI result This seems to vary as per Erlang version, so I took the easy way out :) --- .dialyzer_ignore.exs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.dialyzer_ignore.exs b/.dialyzer_ignore.exs index 2a85af071a..d7c1add030 100644 --- a/.dialyzer_ignore.exs +++ b/.dialyzer_ignore.exs @@ -3,5 +3,6 @@ {":0:unknown_function Function :persistent_term.put/2 does not exist."}, {"lib/absinthe/middleware/async.ex", :unknown_function, 117}, {"lib/absinthe/middleware/batch.ex", :unknown_function, 213}, - {"lib/absinthe/utils/render.ex", :improper_list_constr, 51} + {"lib/absinthe/utils/render.ex", :improper_list_constr, 51}, + {":0:unknown_function Function OpentelemetryProcessPropagator.Task.async/1 does not exist."} ] From 3c2c1f95e26c43b0113ecc2e4a0260bb759670ca Mon Sep 17 00:00:00 2001 From: "Paulo F. Oliveira" Date: Sun, 18 Jun 2023 02:34:58 +0100 Subject: [PATCH 100/120] Bump ex_doc to prevent compilation warnings All changes to mix.lock are due to mix deps.update ex_doc --- mix.lock | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/mix.lock b/mix.lock index a27456d867..4b9eb9f757 100644 --- a/mix.lock +++ b/mix.lock @@ -4,16 +4,16 @@ "decimal": {:hex, :decimal, "2.0.0", "a78296e617b0f5dd4c6caf57c714431347912ffb1d0842e998e9792b5642d697", [:mix], [], "hexpm", "34666e9c55dea81013e77d9d87370fe6cb6291d1ef32f46a1600230b1d44f577"}, "deep_merge": {:hex, :deep_merge, "1.0.0", "b4aa1a0d1acac393bdf38b2291af38cb1d4a52806cf7a4906f718e1feb5ee961", [:mix], [], "hexpm", "ce708e5f094b9cd4e8f2be4f00d2f4250c4095be93f8cd6d018c753894885430"}, "dialyxir": {:hex, :dialyxir, "1.1.0", "c5aab0d6e71e5522e77beff7ba9e08f8e02bad90dfbeffae60eaf0cb47e29488", [:mix], [{:erlex, ">= 0.2.6", [hex: :erlex, repo: "hexpm", optional: false]}], "hexpm", "07ea8e49c45f15264ebe6d5b93799d4dd56a44036cf42d0ad9c960bc266c0b9a"}, - "earmark_parser": {:hex, :earmark_parser, "1.4.26", "f4291134583f373c7d8755566122908eb9662df4c4b63caa66a0eabe06569b0a", [:mix], [], "hexpm", "48d460899f8a0c52c5470676611c01f64f3337bad0b26ddab43648428d94aabc"}, + "earmark_parser": {:hex, :earmark_parser, "1.4.32", "fa739a0ecfa34493de19426681b23f6814573faee95dfd4b4aafe15a7b5b32c6", [:mix], [], "hexpm", "b8b0dd77d60373e77a3d7e8afa598f325e49e8663a51bcc2b88ef41838cca755"}, "erlex": {:hex, :erlex, "0.2.6", "c7987d15e899c7a2f34f5420d2a2ea0d659682c06ac607572df55a43753aa12e", [:mix], [], "hexpm", "2ed2e25711feb44d52b17d2780eabf998452f6efda104877a3881c2f8c0c0c75"}, - "ex_doc": {:hex, :ex_doc, "0.28.5", "3e52a6d2130ce74d096859e477b97080c156d0926701c13870a4e1f752363279", [:mix], [{:earmark_parser, "~> 1.4.19", [hex: :earmark_parser, repo: "hexpm", optional: false]}, {:makeup_elixir, "~> 0.14", [hex: :makeup_elixir, repo: "hexpm", optional: false]}, {:makeup_erlang, "~> 0.1", [hex: :makeup_erlang, repo: "hexpm", optional: false]}], "hexpm", "d2c4b07133113e9aa3e9ba27efb9088ba900e9e51caa383919676afdf09ab181"}, + "ex_doc": {:hex, :ex_doc, "0.29.4", "6257ecbb20c7396b1fe5accd55b7b0d23f44b6aa18017b415cb4c2b91d997729", [:mix], [{:earmark_parser, "~> 1.4.31", [hex: :earmark_parser, repo: "hexpm", optional: false]}, {:makeup_elixir, "~> 0.14", [hex: :makeup_elixir, repo: "hexpm", optional: false]}, {:makeup_erlang, "~> 0.1", [hex: :makeup_erlang, repo: "hexpm", optional: false]}], "hexpm", "2c6699a737ae46cb61e4ed012af931b57b699643b24dabe2400a8168414bc4f5"}, "file_system": {:hex, :file_system, "0.2.10", "fb082005a9cd1711c05b5248710f8826b02d7d1784e7c3451f9c1231d4fc162d", [:mix], [], "hexpm", "41195edbfb562a593726eda3b3e8b103a309b733ad25f3d642ba49696bf715dc"}, "makeup": {:hex, :makeup, "1.1.0", "6b67c8bc2882a6b6a445859952a602afc1a41c2e08379ca057c0f525366fc3ca", [:mix], [{:nimble_parsec, "~> 1.2.2 or ~> 1.3", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "0a45ed501f4a8897f580eabf99a2e5234ea3e75a4373c8a52824f6e873be57a6"}, - "makeup_elixir": {:hex, :makeup_elixir, "0.16.0", "f8c570a0d33f8039513fbccaf7108c5d750f47d8defd44088371191b76492b0b", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}, {:nimble_parsec, "~> 1.2.3", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "28b2cbdc13960a46ae9a8858c4bebdec3c9a6d7b4b9e7f4ed1502f8159f338e7"}, - "makeup_erlang": {:hex, :makeup_erlang, "0.1.1", "3fcb7f09eb9d98dc4d208f49cc955a34218fc41ff6b84df7c75b3e6e533cc65f", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}], "hexpm", "174d0809e98a4ef0b3309256cbf97101c6ec01c4ab0b23e926a9e17df2077cbb"}, + "makeup_elixir": {:hex, :makeup_elixir, "0.16.1", "cc9e3ca312f1cfeccc572b37a09980287e243648108384b97ff2b76e505c3555", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}, {:nimble_parsec, "~> 1.2.3 or ~> 1.3", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "e127a341ad1b209bd80f7bd1620a15693a9908ed780c3b763bccf7d200c767c6"}, + "makeup_erlang": {:hex, :makeup_erlang, "0.1.2", "ad87296a092a46e03b7e9b0be7631ddcf64c790fa68a9ef5323b6cbb36affc72", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}], "hexpm", "f3f5a1ca93ce6e092d92b6d9c049bcda58a3b617a8d888f8e7231c85630e8108"}, "makeup_graphql": {:hex, :makeup_graphql, "0.1.2", "81e2939aab6d2b81d39ee5d9e13fae02599e9ca6e1152e0eeed737a98a5f96aa", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}, {:nimble_parsec, "~> 1.1", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "3390ab04ba388d52a94bbe64ef62aa4d7923ceaffac43ec948f58f631440e8fb"}, "mix_test_watch": {:hex, :mix_test_watch, "1.1.0", "330bb91c8ed271fe408c42d07e0773340a7938d8a0d281d57a14243eae9dc8c3", [:mix], [{:file_system, "~> 0.2.1 or ~> 0.3", [hex: :file_system, repo: "hexpm", optional: false]}], "hexpm", "52b6b1c476cbb70fd899ca5394506482f12e5f6b0d6acff9df95c7f1e0812ec3"}, - "nimble_parsec": {:hex, :nimble_parsec, "1.2.3", "244836e6e3f1200c7f30cb56733fd808744eca61fd182f731eac4af635cc6d0b", [:mix], [], "hexpm", "c8d789e39b9131acf7b99291e93dae60ab48ef14a7ee9d58c6964f59efb570b0"}, + "nimble_parsec": {:hex, :nimble_parsec, "1.3.1", "2c54013ecf170e249e9291ed0a62e5832f70a476c61da16f6aac6dca0189f2af", [:mix], [], "hexpm", "2682e3c0b2eb58d90c6375fc0cc30bc7be06f365bf72608804fb9cffa5e1b167"}, "opentelemetry_api": {:hex, :opentelemetry_api, "1.2.0", "454a35655b4c1924405ef1f3587f2c6f141bf73366b2c5e8a38dcc619b53eaa0", [:mix, :rebar3], [], "hexpm", "9e677c68243de0f70538798072e66e1fb1d4a2ca8888a6eb493c0a41e5480c35"}, "opentelemetry_process_propagator": {:hex, :opentelemetry_process_propagator, "0.2.1", "20ac37648faf7175cade16fda8d58e6f1ff1b7f2a50a8ef9d70a032c41aba315", [:mix, :rebar3], [{:opentelemetry_api, "~> 1.0", [hex: :opentelemetry_api, repo: "hexpm", optional: false]}], "hexpm", "f317237e39636d4f6140afa5d419e85ed3dc9e9a57072e7cd442df42af7b8aac"}, "statistex": {:hex, :statistex, "1.0.0", "f3dc93f3c0c6c92e5f291704cf62b99b553253d7969e9a5fa713e5481cd858a5", [:mix], [], "hexpm", "ff9d8bee7035028ab4742ff52fc80a2aa35cece833cf5319009b52f1b5a86c27"}, From 82118c8a8b155ce4d92a6aebe657b5270c6b2e7f Mon Sep 17 00:00:00 2001 From: Luis Gustavo Beligante Date: Sun, 18 Jun 2023 12:29:28 -0300 Subject: [PATCH 101/120] add map new to de-duplicate multiple entries to the same doc --- lib/absinthe/subscription.ex | 1 + test/absinthe/execution/subscription_test.exs | 40 ++++++++++++++++++- 2 files changed, 40 insertions(+), 1 deletion(-) diff --git a/lib/absinthe/subscription.ex b/lib/absinthe/subscription.ex index d35c4f4b28..9ce3c29f67 100644 --- a/lib/absinthe/subscription.ex +++ b/lib/absinthe/subscription.ex @@ -179,6 +179,7 @@ defmodule Absinthe.Subscription do {doc_id, doc} end) + |> Map.new() end @doc false diff --git a/test/absinthe/execution/subscription_test.exs b/test/absinthe/execution/subscription_test.exs index 50272b5c7b..54e4743042 100644 --- a/test/absinthe/execution/subscription_test.exs +++ b/test/absinthe/execution/subscription_test.exs @@ -49,7 +49,7 @@ defmodule Absinthe.Execution.SubscriptionTest do @behaviour Absinthe.Subscription.Pubsub def start_link() do - Registry.start_link(keys: :unique, name: __MODULE__) + Registry.start_link(keys: :duplicate, name: __MODULE__) end def node_name() do @@ -678,6 +678,44 @@ defmodule Absinthe.Execution.SubscriptionTest do :telemetry.detach(context.test) end + @query """ + subscription { + otherUser { id } + } + """ + test "de-duplicates pushes to the same context" do + documents = + Enum.map(1..5, fn _index -> + {:ok, doc} = run_subscription(@query, Schema, context: %{context_id: "global"}) + doc + end) + + # assert that all documents are the same + first = hd(documents) + assert Enum.all?(documents, &(&1 == first)) + + Absinthe.Subscription.publish( + PubSub, + %{id: "global_user_id"}, + other_user: "*" + ) + + topic_id = first["subscribed"] + + for _i <- 1..5 do + assert_receive( + {:broadcast, + %{ + event: "subscription:data", + result: %{data: %{"otherUser" => %{"id" => "global_user_id"}}}, + topic: ^topic_id + }} + ) + end + + refute_receive({:broadcast, _}) + end + defp run_subscription(query, schema, opts \\ []) do opts = Keyword.update(opts, :context, %{pubsub: PubSub}, &Map.put(&1, :pubsub, PubSub)) From 8d51f8173acdfcfee2eecc3bc27869d591c710e2 Mon Sep 17 00:00:00 2001 From: Ben Wilson Date: Wed, 28 Jun 2023 22:14:53 -0400 Subject: [PATCH 102/120] 1.7.2 --- mix.exs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mix.exs b/mix.exs index 8ea65fcc76..dae75e0822 100644 --- a/mix.exs +++ b/mix.exs @@ -2,7 +2,7 @@ defmodule Absinthe.Mixfile do use Mix.Project @source_url "https://github.com/absinthe-graphql/absinthe" - @version "1.7.1" + @version "1.7.2" def project do [ From 6a2954dcfa4af5bff57ab938a77a87c61f5c95d7 Mon Sep 17 00:00:00 2001 From: Ben Wilson Date: Wed, 28 Jun 2023 22:19:00 -0400 Subject: [PATCH 103/120] changelog update --- CHANGELOG.md | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c20ce375f5..737063619a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,10 @@ # Changelog -## Unreleased +## 1.7.1 + +- Bug Fix: [Place extra error attributes in error extensions field](https://github.com/absinthe-graphql/absinthe/pull/1215) + +## 1.7.2 - Bug Fix: [Validate type references for invalid wrapped types](https://github.com/absinthe-graphql/absinthe/pull/1195) - Feature: [Add `specifiedBy` type system directive](https://github.com/absinthe-graphql/absinthe/pull/1193) From a9be40bb3e3583d32f5f99dcd496c7545465aacf Mon Sep 17 00:00:00 2001 From: Ben Wilson Date: Wed, 28 Jun 2023 23:30:21 -0400 Subject: [PATCH 104/120] fix map sorting bugs --- lib/absinthe/type/built_ins/introspection.ex | 1 + test/absinthe/schema/manipulation_test.exs | 2 +- .../notation/experimental/macro_extensions_test.exs | 8 ++++---- .../schema/notation/experimental/sdl_extensions_test.exs | 6 +++--- 4 files changed, 9 insertions(+), 8 deletions(-) diff --git a/lib/absinthe/type/built_ins/introspection.ex b/lib/absinthe/type/built_ins/introspection.ex index 5ffc20fe53..15ec76fe1a 100644 --- a/lib/absinthe/type/built_ins/introspection.ex +++ b/lib/absinthe/type/built_ins/introspection.ex @@ -334,6 +334,7 @@ defmodule Absinthe.Type.BuiltIns.Introspection do fields |> Map.take(Map.keys(value)) |> Map.values() + |> Enum.sort_by(& &1.identifier) |> Enum.map(&render_default_value(schema, adapter, &1, value)) |> Enum.join(", ") diff --git a/test/absinthe/schema/manipulation_test.exs b/test/absinthe/schema/manipulation_test.exs index ce1066d17f..a438ebffcb 100644 --- a/test/absinthe/schema/manipulation_test.exs +++ b/test/absinthe/schema/manipulation_test.exs @@ -119,7 +119,7 @@ defmodule Absinthe.Schema.ManipulationTest do }, %Schema.EnumValueDefinition{ identifier: :bar, - value: :foo, + value: :bar, name: "BAR", module: __MODULE__, __reference__: Notation.build_reference(__ENV__) diff --git a/test/absinthe/schema/notation/experimental/macro_extensions_test.exs b/test/absinthe/schema/notation/experimental/macro_extensions_test.exs index 917b2a168f..130d9fda3f 100644 --- a/test/absinthe/schema/notation/experimental/macro_extensions_test.exs +++ b/test/absinthe/schema/notation/experimental/macro_extensions_test.exs @@ -166,7 +166,7 @@ defmodule Absinthe.Schema.Notation.Experimental.MacroExtensionsTest do name: "width", type: :integer } - ] = Map.values(object.fields) + ] = Map.values(object.fields) |> Enum.sort_by(& &1.name) assert [:valued_entity] = object.interfaces end @@ -199,7 +199,7 @@ defmodule Absinthe.Schema.Notation.Experimental.MacroExtensionsTest do name: "width", type: :integer } - ] = Map.values(object.fields) + ] = Map.values(object.fields) |> Enum.sort_by(& &1.name) end test "can extend input objects" do @@ -214,7 +214,7 @@ defmodule Absinthe.Schema.Notation.Experimental.MacroExtensionsTest do name: "y", type: :float } - ] = Map.values(object.fields) + ] = Map.values(object.fields) |> Enum.sort_by(& &1.name) end test "can extend interfaces" do @@ -237,7 +237,7 @@ defmodule Absinthe.Schema.Notation.Experimental.MacroExtensionsTest do name: "value", type: :integer } - ] = Map.values(object.fields) + ] = Map.values(object.fields) |> Enum.sort_by(& &1.name) assert [:valued_entity] = object.interfaces end diff --git a/test/absinthe/schema/notation/experimental/sdl_extensions_test.exs b/test/absinthe/schema/notation/experimental/sdl_extensions_test.exs index bcf85d3139..4bfa838744 100644 --- a/test/absinthe/schema/notation/experimental/sdl_extensions_test.exs +++ b/test/absinthe/schema/notation/experimental/sdl_extensions_test.exs @@ -162,7 +162,7 @@ defmodule Absinthe.Schema.Notation.Experimental.SdlExtensionsTest do name: "width", type: :integer } - ] = Map.values(object.fields) + ] = Map.values(object.fields) |> Enum.sort_by(& &1.name) assert [:valued_entity] = object.interfaces end @@ -179,7 +179,7 @@ defmodule Absinthe.Schema.Notation.Experimental.SdlExtensionsTest do name: "y", type: :float } - ] = Map.values(object.fields) + ] = Map.values(object.fields) |> Enum.sort_by(& &1.name) end test "can extend interfaces" do @@ -202,7 +202,7 @@ defmodule Absinthe.Schema.Notation.Experimental.SdlExtensionsTest do name: "value", type: :integer } - ] = Map.values(object.fields) + ] = Map.values(object.fields) |> Enum.sort_by(& &1.name) assert [:valued_entity] = object.interfaces end From 72b09fb2a50abf057f0757f1eb3c36deee7e7b12 Mon Sep 17 00:00:00 2001 From: Ben Wilson Date: Wed, 28 Jun 2023 23:30:58 -0400 Subject: [PATCH 105/120] OTP 26 and Elixir 15 support --- .github/workflows/elixir.yml | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/.github/workflows/elixir.yml b/.github/workflows/elixir.yml index 080b8d90d0..3440123392 100644 --- a/.github/workflows/elixir.yml +++ b/.github/workflows/elixir.yml @@ -16,12 +16,14 @@ jobs: elixir: - "1.13" - "1.14" + - "1.15" otp: - "24" - "25" + - "26" include: - - elixir: "1.14" - otp: "25" + - elixir: "1.15" + otp: "26" format: true steps: From 4d7449e0163b4441a7e1930ca5f51264381226b2 Mon Sep 17 00:00:00 2001 From: Ben Wilson Date: Wed, 28 Jun 2023 23:31:11 -0400 Subject: [PATCH 106/120] mix format --- lib/absinthe/lexer.ex | 6 ++--- lib/absinthe/middleware/async.ex | 4 +-- lib/absinthe/middleware/batch.ex | 4 +-- lib/absinthe/resolution/helpers.ex | 4 +-- .../execution/operation_by_name_test.exs | 4 ++- test/absinthe/lexer_test.exs | 26 +++++++++---------- 6 files changed, 25 insertions(+), 23 deletions(-) diff --git a/lib/absinthe/lexer.ex b/lib/absinthe/lexer.ex index 0af0d1acbc..e16663bf45 100644 --- a/lib/absinthe/lexer.ex +++ b/lib/absinthe/lexer.ex @@ -362,7 +362,7 @@ defmodule Absinthe.Lexer do ) ) - defp fill_mantissa(rest, raw, context, _, _), do: {rest, '0.' ++ raw, context} + defp fill_mantissa(rest, raw, context, _, _), do: {rest, ~c"0." ++ raw, context} defp unescape_unicode(rest, content, context, _loc, _) do code = content |> Enum.reverse() @@ -453,7 +453,7 @@ defmodule Absinthe.Lexer do defp block_string_value_token(rest, chars, context, _loc, _byte_offset) do context = Map.update(context, :token_count, 1, &(&1 + 1)) - value = '"""' ++ (chars |> Enum.reverse()) ++ '"""' + value = ~c"\"\"\"" ++ (chars |> Enum.reverse()) ++ ~c"\"\"\"" {rest, [{:block_string_value, context.token_location, value}], Map.delete(context, :token_location)} @@ -465,7 +465,7 @@ defmodule Absinthe.Lexer do defp string_value_token(rest, chars, context, _loc, _byte_offset) do context = Map.update(context, :token_count, 1, &(&1 + 1)) - value = '"' ++ tl(chars |> Enum.reverse()) ++ '"' + value = ~c"\"" ++ tl(chars |> Enum.reverse()) ++ ~c"\"" {rest, [{:string_value, context.token_location, value}], Map.delete(context, :token_location)} end diff --git a/lib/absinthe/middleware/async.ex b/lib/absinthe/middleware/async.ex index c121af0481..52bdaccdb1 100644 --- a/lib/absinthe/middleware/async.ex +++ b/lib/absinthe/middleware/async.ex @@ -113,10 +113,10 @@ defmodule Absinthe.Middleware.Async do # Optionally use `async/1` function from `opentelemetry_process_propagator` if available if Code.ensure_loaded?(OpentelemetryProcessPropagator.Task) do - @spec async((() -> any)) :: Task.t() + @spec async((-> any)) :: Task.t() defdelegate async(fun), to: OpentelemetryProcessPropagator.Task else - @spec async((() -> any)) :: Task.t() + @spec async((-> any)) :: Task.t() defdelegate async(fun), to: Task end end diff --git a/lib/absinthe/middleware/batch.ex b/lib/absinthe/middleware/batch.ex index 4ab60719b1..1030fbb098 100644 --- a/lib/absinthe/middleware/batch.ex +++ b/lib/absinthe/middleware/batch.ex @@ -209,10 +209,10 @@ defmodule Absinthe.Middleware.Batch do # Optionally use `async/1` function from `opentelemetry_process_propagator` if available if Code.ensure_loaded?(OpentelemetryProcessPropagator.Task) do - @spec async((() -> any)) :: Task.t() + @spec async((-> any)) :: Task.t() defdelegate async(fun), to: OpentelemetryProcessPropagator.Task else - @spec async((() -> any)) :: Task.t() + @spec async((-> any)) :: Task.t() defdelegate async(fun), to: Task end end diff --git a/lib/absinthe/resolution/helpers.ex b/lib/absinthe/resolution/helpers.ex index 1746b27c94..70f1e21738 100644 --- a/lib/absinthe/resolution/helpers.ex +++ b/lib/absinthe/resolution/helpers.ex @@ -34,8 +34,8 @@ defmodule Absinthe.Resolution.Helpers do end ``` """ - @spec async((() -> term)) :: {:middleware, Middleware.Async, term} - @spec async((() -> term), opts :: [{:timeout, pos_integer}]) :: + @spec async((-> term)) :: {:middleware, Middleware.Async, term} + @spec async((-> term), opts :: [{:timeout, pos_integer}]) :: {:middleware, Middleware.Async, term} def async(fun, opts \\ []) do {:middleware, Middleware.Async, {fun, opts}} diff --git a/test/absinthe/integration/execution/operation_by_name_test.exs b/test/absinthe/integration/execution/operation_by_name_test.exs index a67a17df38..fcdc6a8860 100644 --- a/test/absinthe/integration/execution/operation_by_name_test.exs +++ b/test/absinthe/integration/execution/operation_by_name_test.exs @@ -55,7 +55,9 @@ defmodule Elixir.Absinthe.Integration.Execution.OperationByNameTest do test "scenario #4" do assert {:ok, %{data: %{"thing" => %{"name" => "Bar"}}}} == - Absinthe.run(@query, Absinthe.Fixtures.Things.MacroSchema, operation_name: "ThingBar") + Absinthe.run(@query, Absinthe.Fixtures.Things.MacroSchema, + operation_name: "ThingBar" + ) end @query """ diff --git a/test/absinthe/lexer_test.exs b/test/absinthe/lexer_test.exs index 592399f6ba..fc90551769 100644 --- a/test/absinthe/lexer_test.exs +++ b/test/absinthe/lexer_test.exs @@ -5,7 +5,7 @@ defmodule Absinthe.LexerTest do { foo } """ test "basic document" do - assert {:ok, [{:"{", {1, 1}}, {:name, {1, 3}, 'foo'}, {:"}", {1, 7}}]} = + assert {:ok, [{:"{", {1, 1}}, {:name, {1, 3}, ~c"foo"}, {:"}", {1, 7}}]} = Absinthe.Lexer.tokenize(@query) end @@ -13,7 +13,7 @@ defmodule Absinthe.LexerTest do { nullName } """ test "document with a name that starts with a keyword" do - assert {:ok, [{:"{", {1, 1}}, {:name, {1, 3}, 'nullName'}, {:"}", {1, 12}}]} = + assert {:ok, [{:"{", {1, 1}}, {:name, {1, 3}, ~c"nullName"}, {:"}", {1, 12}}]} = Absinthe.Lexer.tokenize(@query) end @@ -23,7 +23,7 @@ defmodule Absinthe.LexerTest do } """ test "basic document, multiple lines" do - assert {:ok, [{:"{", {1, 1}}, {:name, {2, 3}, 'foo'}, {:"}", {3, 1}}]} = + assert {:ok, [{:"{", {1, 1}}, {:name, {2, 3}, ~c"foo"}, {:"}", {3, 1}}]} = Absinthe.Lexer.tokenize(@query) end @@ -38,9 +38,9 @@ defmodule Absinthe.LexerTest do [ {:"{", {1, 1}}, {:"{", {2, 3}}, - {:name, {2, 5}, 'foo'}, + {:name, {2, 5}, ~c"foo"}, {:"(", {2, 8}}, - {:name, {2, 9}, 'bar'}, + {:name, {2, 9}, ~c"bar"}, {:":", {2, 12}}, {:string_value, {2, 14}, ~S("\\FOO") |> String.to_charlist()}, {:")", {2, 23}}, @@ -60,11 +60,11 @@ defmodule Absinthe.LexerTest do assert {:ok, [ {:"{", {1, 1}}, - {:name, {2, 3}, 'foo'}, + {:name, {2, 3}, ~c"foo"}, {:"(", {2, 6}}, - {:name, {2, 7}, 'bar'}, + {:name, {2, 7}, ~c"bar"}, {:":", {2, 10}}, - {:block_string_value, {2, 12}, '"""\n stuff\n """'}, + {:block_string_value, {2, 12}, ~c"\"\"\"\n stuff\n \"\"\""}, {:")", {4, 6}}, {:"}", {5, 1}} ]} = Absinthe.Lexer.tokenize(@query) @@ -82,15 +82,15 @@ defmodule Absinthe.LexerTest do test "document with emojis" do assert {:ok, [ - {:block_string_value, {2, 1}, '"""\nA block quote with a 👍 emoji.\n"""'}, + {:block_string_value, {2, 1}, ~c"\"\"\"\nA block quote with a 👍 emoji.\n\"\"\""}, {:"{", {5, 1}}, - {:name, {6, 3}, 'foo'}, + {:name, {6, 3}, ~c"foo"}, {:"(", {6, 6}}, - {:name, {6, 7}, 'bar'}, + {:name, {6, 7}, ~c"bar"}, {:":", {6, 10}}, - {:string_value, {6, 12}, '"A string with a 🎉 emoji."'}, + {:string_value, {6, 12}, ~c"\"A string with a 🎉 emoji.\""}, {:")", {6, 38}}, - {:name, {6, 40}, 'anotherOnSameLine'}, + {:name, {6, 40}, ~c"anotherOnSameLine"}, {:"}", {7, 1}} ]} == Absinthe.Lexer.tokenize(@query) end From 3e476d35fb45c917c61fa7290f62b3f7e8f82b9e Mon Sep 17 00:00:00 2001 From: Ben Wilson Date: Wed, 28 Jun 2023 23:33:06 -0400 Subject: [PATCH 107/120] only do OTP 26 on Elixir 1.15 --- .github/workflows/elixir.yml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/.github/workflows/elixir.yml b/.github/workflows/elixir.yml index 3440123392..aa3fb1caf0 100644 --- a/.github/workflows/elixir.yml +++ b/.github/workflows/elixir.yml @@ -25,6 +25,11 @@ jobs: - elixir: "1.15" otp: "26" format: true + exclude: + - otp: 26 + elixir: 1.13 + - otp: 26 + elixir: 1.14 steps: - name: Checkout From 52c7e1691a1d1aa366b5f13a7918bbac8563b58b Mon Sep 17 00:00:00 2001 From: Ben Wilson Date: Wed, 28 Jun 2023 23:38:56 -0400 Subject: [PATCH 108/120] remove dialyxer from the CI for now --- .github/workflows/elixir.yml | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/.github/workflows/elixir.yml b/.github/workflows/elixir.yml index aa3fb1caf0..11b4af428e 100644 --- a/.github/workflows/elixir.yml +++ b/.github/workflows/elixir.yml @@ -71,16 +71,16 @@ jobs: env: SCHEMA_PROVIDER: persistent_term - - name: Cache/uncache PLTs - uses: actions/cache@v3 - with: - path: | - priv/plts - key: "${{ runner.os }}-\ - erlang-${{ matrix.otp }}-\ - elixir-${{ matrix.elixir }}-\ - ${{ hashFiles('mix.lock') }}" + # - name: Cache/uncache PLTs + # uses: actions/cache@v3 + # with: + # path: | + # priv/plts + # key: "${{ runner.os }}-\ + # erlang-${{ matrix.otp }}-\ + # elixir-${{ matrix.elixir }}-\ + # ${{ hashFiles('mix.lock') }}" - - name: Run Dialyzer - run: mix dialyzer + # - name: Run Dialyzer + # run: mix dialyzer From ddd2a310337c36a587a4d7c3490e4f7052a7ae14 Mon Sep 17 00:00:00 2001 From: Ben Wilson Date: Wed, 28 Jun 2023 23:44:44 -0400 Subject: [PATCH 109/120] version bump --- CHANGELOG.md | 4 +++- mix.exs | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 737063619a..2bca0e80d1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,7 +1,9 @@ # Changelog -## 1.7.1 +## 1.7.3 +- Bug Fix: [OTP 26 and Elixir 1.15 tweaks]https://github.com/absinthe-graphql/absinthe/pull/1253 +- Bug Fix: [OTP 25 twaks]https://github.com/absinthe-graphql/absinthe/pull/1253 - Bug Fix: [Place extra error attributes in error extensions field](https://github.com/absinthe-graphql/absinthe/pull/1215) ## 1.7.2 diff --git a/mix.exs b/mix.exs index 77c4e88de5..ea58e5c5c1 100644 --- a/mix.exs +++ b/mix.exs @@ -2,7 +2,7 @@ defmodule Absinthe.Mixfile do use Mix.Project @source_url "https://github.com/absinthe-graphql/absinthe" - @version "1.7.2" + @version "1.7.3" def project do [ From 7d75be76654dd573eafcc79d6ac1a2a0bb51a20b Mon Sep 17 00:00:00 2001 From: Ben Wilson Date: Wed, 28 Jun 2023 23:45:40 -0400 Subject: [PATCH 110/120] remove warning --- lib/absinthe/phase/parse.ex | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/absinthe/phase/parse.ex b/lib/absinthe/phase/parse.ex index d1c0e25a42..a6a58a18b2 100644 --- a/lib/absinthe/phase/parse.ex +++ b/lib/absinthe/phase/parse.ex @@ -117,7 +117,7 @@ defmodule Absinthe.Phase.Parse do @spec format_raw_parse_error(map) :: Phase.Error.t() defp format_raw_parse_error(%{} = error) do detail = - if Exception.exception?(error) do + if is_exception(error) do ": " <> Exception.message(error) else "" From 2ee47cdc0102a802385e652407b4bac7c2bc665c Mon Sep 17 00:00:00 2001 From: Luis Gustavo Beligante Date: Thu, 29 Jun 2023 07:59:34 -0300 Subject: [PATCH 111/120] dedup documents before unpacking processing --- lib/absinthe/subscription.ex | 2 +- test/absinthe/execution/subscription_test.exs | 5 ++--- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/lib/absinthe/subscription.ex b/lib/absinthe/subscription.ex index 9ce3c29f67..63d28bccfe 100644 --- a/lib/absinthe/subscription.ex +++ b/lib/absinthe/subscription.ex @@ -173,13 +173,13 @@ defmodule Absinthe.Subscription do pubsub |> registry_name |> Registry.lookup(key) + |> Enum.uniq_by(fn {_, {doc_id, _doc}} -> doc_id end) |> Enum.map(fn match -> {_, {doc_id, doc}} = match doc = Map.update!(doc, :initial_phases, &PipelineSerializer.unpack/1) {doc_id, doc} end) - |> Map.new() end @doc false diff --git a/test/absinthe/execution/subscription_test.exs b/test/absinthe/execution/subscription_test.exs index 54e4743042..d6fd6d42b2 100644 --- a/test/absinthe/execution/subscription_test.exs +++ b/test/absinthe/execution/subscription_test.exs @@ -691,8 +691,7 @@ defmodule Absinthe.Execution.SubscriptionTest do end) # assert that all documents are the same - first = hd(documents) - assert Enum.all?(documents, &(&1 == first)) + assert [document] = Enum.dedup(documents) Absinthe.Subscription.publish( PubSub, @@ -700,7 +699,7 @@ defmodule Absinthe.Execution.SubscriptionTest do other_user: "*" ) - topic_id = first["subscribed"] + topic_id = document["subscribed"] for _i <- 1..5 do assert_receive( From 97ea4dc1ca699c47547496412b7d443e52092533 Mon Sep 17 00:00:00 2001 From: Luis Gustavo Beligante Date: Thu, 29 Jun 2023 16:52:26 -0300 Subject: [PATCH 112/120] refact: use map.new to save some traversals --- lib/absinthe/subscription.ex | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/lib/absinthe/subscription.ex b/lib/absinthe/subscription.ex index 63d28bccfe..7563e5f064 100644 --- a/lib/absinthe/subscription.ex +++ b/lib/absinthe/subscription.ex @@ -173,9 +173,7 @@ defmodule Absinthe.Subscription do pubsub |> registry_name |> Registry.lookup(key) - |> Enum.uniq_by(fn {_, {doc_id, _doc}} -> doc_id end) - |> Enum.map(fn match -> - {_, {doc_id, doc}} = match + |> Map.new(fn {_, {doc_id, doc}} -> doc = Map.update!(doc, :initial_phases, &PipelineSerializer.unpack/1) {doc_id, doc} From 66c21a1e117747de341d3960d52f6917b39c7df2 Mon Sep 17 00:00:00 2001 From: "Paulo F. Oliveira" Date: Fri, 30 Jun 2023 20:27:18 +0100 Subject: [PATCH 113/120] Adapt to 1.15's code path pruning, for dialyxir'ing purposes --- mix.exs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/mix.exs b/mix.exs index ea58e5c5c1..88c2cc03ff 100644 --- a/mix.exs +++ b/mix.exs @@ -33,7 +33,8 @@ defmodule Absinthe.Mixfile do plt_add_deps: :apps_direct, plt_file: {:no_warn, "priv/plts/project.plt"}, plt_add_apps: [:mix, :dataloader, :decimal, :ex_unit] - ] + ], + prune_code_paths: prune_code_paths(Mix.env()) ] end @@ -85,6 +86,9 @@ defmodule Absinthe.Mixfile do ] end + defp prune_code_paths(:test), do: false + defp prune_code_paths(_), do: true + # # Documentation # From 301ab7ab9b6629bef4b155ffb8db4cbe8364dcc3 Mon Sep 17 00:00:00 2001 From: Ben Wilson Date: Fri, 30 Jun 2023 09:30:22 -0400 Subject: [PATCH 114/120] try a cache prefix --- .github/workflows/elixir.yml | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/.github/workflows/elixir.yml b/.github/workflows/elixir.yml index 11b4af428e..b4e2c7dfb1 100644 --- a/.github/workflows/elixir.yml +++ b/.github/workflows/elixir.yml @@ -71,16 +71,16 @@ jobs: env: SCHEMA_PROVIDER: persistent_term - # - name: Cache/uncache PLTs - # uses: actions/cache@v3 - # with: - # path: | - # priv/plts - # key: "${{ runner.os }}-\ - # erlang-${{ matrix.otp }}-\ - # elixir-${{ matrix.elixir }}-\ - # ${{ hashFiles('mix.lock') }}" + - name: Cache/uncache PLTs + uses: actions/cache@v3 + with: + path: | + priv/plts + key: "v1-${{ runner.os }}-\ + erlang-${{ matrix.otp }}-\ + elixir-${{ matrix.elixir }}-\ + ${{ hashFiles('mix.lock') }}" - # - name: Run Dialyzer - # run: mix dialyzer + - name: Run Dialyzer + run: mix dialyzer From bf80f04fc8155499978b4c7a5a2309116bcf95e4 Mon Sep 17 00:00:00 2001 From: Ben Wilson Date: Fri, 30 Jun 2023 17:45:25 -0400 Subject: [PATCH 115/120] changelog --- CHANGELOG.md | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2bca0e80d1..99f8e61548 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,9 +1,13 @@ # Changelog +## (1.7.4) Unreleased + +- Bug Fix: [Bugfix: multiple pushes per client for subscriptions that have a context_id](https://github.com/absinthe-graphql/absinthe/pull/1249) + ## 1.7.3 -- Bug Fix: [OTP 26 and Elixir 1.15 tweaks]https://github.com/absinthe-graphql/absinthe/pull/1253 -- Bug Fix: [OTP 25 twaks]https://github.com/absinthe-graphql/absinthe/pull/1253 +- Bug Fix: [OTP 26 and Elixir 1.15 tweaks](https://github.com/absinthe-graphql/absinthe/pull/1253) +- Bug Fix: [OTP 25 tweaks](https://github.com/absinthe-graphql/absinthe/pull/1253) - Bug Fix: [Place extra error attributes in error extensions field](https://github.com/absinthe-graphql/absinthe/pull/1215) ## 1.7.2 From 3c102f044138c3edc86c45a989bba6b7da5d9361 Mon Sep 17 00:00:00 2001 From: Ben Wilson Date: Fri, 7 Jul 2023 11:56:48 -0400 Subject: [PATCH 116/120] v1.7.4 --- CHANGELOG.md | 2 +- mix.exs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 99f8e61548..21d2f0e10a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,6 @@ # Changelog -## (1.7.4) Unreleased +## 1.7.4 - Bug Fix: [Bugfix: multiple pushes per client for subscriptions that have a context_id](https://github.com/absinthe-graphql/absinthe/pull/1249) diff --git a/mix.exs b/mix.exs index 88c2cc03ff..968c0dd2a3 100644 --- a/mix.exs +++ b/mix.exs @@ -2,7 +2,7 @@ defmodule Absinthe.Mixfile do use Mix.Project @source_url "https://github.com/absinthe-graphql/absinthe" - @version "1.7.3" + @version "1.7.4" def project do [ From fa9c8b68b88e88e4fb073d87681812a15e22ce1f Mon Sep 17 00:00:00 2001 From: Ben Wilson Date: Mon, 24 Jul 2023 08:34:37 -0400 Subject: [PATCH 117/120] support dataloader 2.0 --- CHANGELOG.md | 4 ++++ mix.exs | 4 ++-- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 21d2f0e10a..dbaa240b92 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,10 @@ ## 1.7.4 +- Feature: Support Dataloader 2.0 + +## 1.7.4 + - Bug Fix: [Bugfix: multiple pushes per client for subscriptions that have a context_id](https://github.com/absinthe-graphql/absinthe/pull/1249) ## 1.7.3 diff --git a/mix.exs b/mix.exs index 968c0dd2a3..c37d6ce184 100644 --- a/mix.exs +++ b/mix.exs @@ -2,7 +2,7 @@ defmodule Absinthe.Mixfile do use Mix.Project @source_url "https://github.com/absinthe-graphql/absinthe" - @version "1.7.4" + @version "1.7.5" def project do [ @@ -75,7 +75,7 @@ defmodule Absinthe.Mixfile do [ {:nimble_parsec, "~> 1.2.2 or ~> 1.3.0"}, {:telemetry, "~> 1.0 or ~> 0.4"}, - {:dataloader, "~> 1.0.0", optional: true}, + {:dataloader, "~> 1.0.0 or ~> 2.0", optional: true}, {:decimal, "~> 1.0 or ~> 2.0", optional: true}, {:opentelemetry_process_propagator, "~> 0.2.1", optional: true}, {:ex_doc, "~> 0.22", only: :dev}, From 0f3cdb0a1088571b5b0c81da9f98063370f61b66 Mon Sep 17 00:00:00 2001 From: Ben Wilson Date: Tue, 14 Jul 2020 11:06:57 -0400 Subject: [PATCH 118/120] fix null propagation with non_null(list_of(non_null(type))) --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index dbaa240b92..be0046bd81 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -130,6 +130,10 @@ too impactful for a patch release. - Bug Fix: Handle null propagation with `non_null(list_of(non_null(type)))` properly - Bug Fix: Fix [double escaping issue](https://github.com/absinthe-graphql/absinthe/pull/962) with string literal arguments. +## 1.5.3 (unreleased) + +- Bug Fix: Handle null propagation with `non_null(list_of(non_null(type)))` properly + ## 1.5.2 - Bug Fix: Fix issue with persistent term backend. From 4259e1695ebca7bf87c584d78b3189899e5cad74 Mon Sep 17 00:00:00 2001 From: Jake Robers Date: Wed, 28 Sep 2022 09:14:31 -0500 Subject: [PATCH 119/120] fix(phase/variable_types_match): soft fail with Logger.warn Patches breaking change introduced in 1.7 since type mismatches throws errors beginning in 1.7. This patch does a soft fail (through Logger.warn) for newer validation logic while preserving the old validation logic. --- .../arguments/variable_types_match.ex | 62 ++++++++++++++++++- .../execution/input_object_test.exs | 2 +- .../variables_of_correct_type_test.exs | 9 ++- 3 files changed, 68 insertions(+), 5 deletions(-) diff --git a/lib/absinthe/phase/document/arguments/variable_types_match.ex b/lib/absinthe/phase/document/arguments/variable_types_match.ex index 2ba712e427..b969715a67 100644 --- a/lib/absinthe/phase/document/arguments/variable_types_match.ex +++ b/lib/absinthe/phase/document/arguments/variable_types_match.ex @@ -7,6 +7,8 @@ defmodule Absinthe.Phase.Document.Arguments.VariableTypesMatch do use Absinthe.Phase + require Logger + alias Absinthe.Blueprint alias Absinthe.Blueprint.Document.{Operation, Fragment} alias Absinthe.Type @@ -47,12 +49,67 @@ defmodule Absinthe.Phase.Document.Arguments.VariableTypesMatch do def check_variable_types(%Operation{} = op) do variable_defs = Map.new(op.variable_definitions, &{&1.name, &1}) + + # This is the new/breaking implementation which will now only log warnings + # for type mismatches. Blueprint.prewalk(op, &check_variable_type(&1, op.name, variable_defs)) + + # This is the legacy implementation that we will continue to use until + # everything is patched. + Blueprint.prewalk(op, &check_var_type(&1, op.name, variable_defs)) end def check_variable_types(%Operation{} = op, %Fragment.Named{} = fragment) do variable_defs = Map.new(op.variable_definitions, &{&1.name, &1}) + + # This is the new/breaking implementation which will now only log warnings + # for type mismatches. Blueprint.prewalk(fragment, &check_variable_type(&1, op.name, variable_defs)) + + # This is the legacy implementation that we will continue to use until + # everything is patched. + Blueprint.prewalk(fragment, &check_var_type(&1, op.name, variable_defs)) + end + + defp check_var_type(%{schema_node: nil} = node, _, _) do + {:halt, node} + end + + defp check_var_type( + %Blueprint.Input.Value{ + raw: %{content: %Blueprint.Input.Variable{} = var}, + schema_node: schema_node + } = node, + op_name, + variable_defs + ) do + case Map.fetch(variable_defs, var.name) do + {:ok, %{schema_node: var_schema_type}} -> + # null vs not null is handled elsewhere + var_schema_type = Absinthe.Type.unwrap(var_schema_type) + arg_schema_type = Absinthe.Type.unwrap(schema_node) + + if var_schema_type && arg_schema_type && var_schema_type.name != arg_schema_type.name do + # error + var_with_error = + put_error(var, %Absinthe.Phase.Error{ + phase: __MODULE__, + message: error_message(op_name, var, var_schema_type.name, arg_schema_type.name), + locations: [var.source_location] + }) + + {:halt, put_in(node.raw.content, var_with_error)} + else + node + end + + _ -> + node + end + end + + defp check_var_type(node, _, _) do + node end defp check_variable_type(%{schema_node: nil} = node, _, _) do @@ -81,12 +138,15 @@ defmodule Absinthe.Phase.Document.Arguments.VariableTypesMatch do ) do node else + variable_error = error(operation_name, variable, variable_definition, location_type) + variable = put_error( variable, - error(operation_name, variable, variable_definition, location_type) + variable_error ) + Logger.warn("VariableTypesMatch: #{variable_error.message}") {:halt, put_in(node.input_value.raw.content, variable)} end diff --git a/test/absinthe/integration/execution/input_object_test.exs b/test/absinthe/integration/execution/input_object_test.exs index 2bd1b7ae75..8dda10961b 100644 --- a/test/absinthe/integration/execution/input_object_test.exs +++ b/test/absinthe/integration/execution/input_object_test.exs @@ -35,7 +35,7 @@ defmodule Elixir.Absinthe.Integration.Execution.InputObjectTest do %{ locations: [%{column: 33, line: 2}], message: - "Variable `$input` of type `Boolean` found as input to argument of type `InputThing!`." + "Variable `$input` of type `Boolean` found as input to argument of type `InputThing`." } ] }} == diff --git a/test/absinthe/phase/document/validation/variables_of_correct_type_test.exs b/test/absinthe/phase/document/validation/variables_of_correct_type_test.exs index f7cb666837..e03c4fcfb0 100644 --- a/test/absinthe/phase/document/validation/variables_of_correct_type_test.exs +++ b/test/absinthe/phase/document/validation/variables_of_correct_type_test.exs @@ -22,7 +22,7 @@ defmodule Absinthe.Phase.Document.Validation.VariablesOfCorrectTypeTest do variables: %{"intArg" => 5} ) - expected_error_msg = error_message("test", "intArg", "Int!", "String") + expected_error_msg = error_message("test", "intArg", "Int", "String") assert expected_error_msg in (errors |> Enum.map(& &1.message)) end @@ -40,8 +40,7 @@ defmodule Absinthe.Phase.Document.Validation.VariablesOfCorrectTypeTest do variables: %{"intArg" => 5} ) - expected_error_msg = error_message("test", "intArg", "DoesNotExist!", "String") - + expected_error_msg = "Argument \"stringArg\" has invalid value $intArg." assert expected_error_msg in (errors |> Enum.map(& &1.message)) end @@ -85,6 +84,10 @@ defmodule Absinthe.Phase.Document.Validation.VariablesOfCorrectTypeTest do assert expected_error_msg in (errors |> Enum.map(& &1.message)) end + # This is one of the cases that the breaking change fixes (and correctly + # validates). Re-enable this test once we use the latest version of + # Absinthe.Phase.Document.Arguments.VariableTypesMatch + @tag :skip test "non null types of variables match non null types of arguments" do {:ok, %{errors: errors}} = Absinthe.run( From 6837b467791ff3ba94bb22c2286e6b59b47f06c3 Mon Sep 17 00:00:00 2001 From: Clifton McIntosh Date: Fri, 17 Nov 2023 15:08:56 -0600 Subject: [PATCH 120/120] Warn on invalid wrapped arguments --- .../phase/validation/known_type_names.ex | 19 ++++++++++++++- .../validation/known_type_names_test.exs | 23 +++++++++++++++++-- 2 files changed, 39 insertions(+), 3 deletions(-) diff --git a/lib/absinthe/phase/validation/known_type_names.ex b/lib/absinthe/phase/validation/known_type_names.ex index bd46972826..9d3067811a 100644 --- a/lib/absinthe/phase/validation/known_type_names.ex +++ b/lib/absinthe/phase/validation/known_type_names.ex @@ -11,6 +11,8 @@ defmodule Absinthe.Phase.Validation.KnownTypeNames do # } # ``` + require Logger + alias Absinthe.{Blueprint, Phase} alias Absinthe.Phase.Document.Validation.Utils @@ -33,7 +35,7 @@ defmodule Absinthe.Phase.Validation.KnownTypeNames do |> put_error(error(node, name)) end - defp handle_node(%Blueprint.Document.VariableDefinition{} = node, schema) do + defp handle_node(%Blueprint.Document.VariableDefinition{schema_node: nil} = node, schema) do name = Blueprint.TypeReference.unwrap(node.type).name inner_schema_type = schema.__absinthe_lookup__(name) @@ -48,6 +50,21 @@ defmodule Absinthe.Phase.Validation.KnownTypeNames do end end + defp handle_node(%Blueprint.Document.VariableDefinition{} = node, schema) do + name = Blueprint.TypeReference.unwrap(node.type).name + inner_schema_type = schema.__absinthe_lookup__(name) + + if inner_schema_type do + node + else + suggestions = suggested_type_names(schema, name) + error = error(node, name, suggestions) + + Logger.warn("KnownTypeNames error: #{error.message}") + node + end + end + defp handle_node(node, _) do node end diff --git a/test/absinthe/phase/validation/known_type_names_test.exs b/test/absinthe/phase/validation/known_type_names_test.exs index 9d6f5db94f..bf71b3be46 100644 --- a/test/absinthe/phase/validation/known_type_names_test.exs +++ b/test/absinthe/phase/validation/known_type_names_test.exs @@ -63,7 +63,7 @@ defmodule Absinthe.Phase.Validation.KnownTypeNamesTest do test "unknown type names are invalid" do assert_fails_validation( """ - query Foo($var: JumbledUpLetters, $foo: Boolen!, $bar: [Bar!]) { + query Foo($var: JumbledUpLetters, $foo: Boolen, $bar: [Bar!]) { user(id: 4) { name pets { ... on Badger { name }, ...PetFields } @@ -82,13 +82,32 @@ defmodule Absinthe.Phase.Validation.KnownTypeNamesTest do 1, ~s(Unknown type "Boolen". Did you mean "Alien" or "Boolean"?) ), - unknown_type(:variable_definition, "Bar", 1), unknown_type(:inline_type_condition, "Badger", 4), unknown_type(:named_type_condition, "Peettt", 7) ] ) end + test "does not invalidate wrapped invalid type names" do + assert_fails_validation( + """ + query Foo($var: JumbledUpLetters, $foo: Boolen!, $bar: [Bar!]) { + user(id: 4) { + name + pets { ... on Badger { name }, ...PetFields } + } + } + fragment PetFields on Peettt { + name + } + """, + [], + [ + unknown_type(:variable_definition, "JumbledUpLetters", 1) + ] + ) + end + test "ignores type definitions" do assert_fails_validation( """