-
Notifications
You must be signed in to change notification settings - Fork 527
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Subscription priming/ordinals #1168
base: main
Are you sure you want to change the base?
Changes from 4 commits
d40b6d9
96e803c
27441d0
ddfa4ee
b0cd04e
6601cc5
5512978
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
defmodule Absinthe.Blueprint.Continuation do | ||
@moduledoc false | ||
|
||
# Continuations allow further resolutions after the initial result is | ||
# returned | ||
|
||
alias Absinthe.Pipeline | ||
|
||
defstruct [ | ||
:phase_input, | ||
:pipeline | ||
] | ||
|
||
@type t :: %__MODULE__{ | ||
phase_input: Pipeline.data_t(), | ||
pipeline: Pipeline.t() | ||
} | ||
end |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,42 @@ | ||
defmodule Absinthe.Phase.Subscription.GetOrdinal do | ||
use Absinthe.Phase | ||
|
||
alias Absinthe.Phase.Subscription.SubscribeSelf | ||
|
||
@moduledoc false | ||
|
||
alias Absinthe.Blueprint | ||
|
||
@spec run(any, Keyword.t()) :: {:ok, Blueprint.t()} | ||
def run(blueprint, _options \\ []) do | ||
op = Blueprint.current_operation(blueprint) | ||
|
||
if op.type == :subscription do | ||
{:ok, | ||
%{blueprint | result: Map.put(blueprint.result, :ordinal, get_ordinal(op, blueprint))}} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I would prefer that we did not add the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yep, makes sense. I've made that change. |
||
else | ||
{:ok, blueprint} | ||
end | ||
end | ||
|
||
defp get_ordinal(op, blueprint) do | ||
%{selections: [field]} = op | ||
{:ok, config} = SubscribeSelf.get_config(field, blueprint.execution.context, blueprint) | ||
|
||
case config[:ordinal] do | ||
nil -> | ||
nil | ||
|
||
fun when is_function(fun, 1) -> | ||
fun.(blueprint.execution.root_value) | ||
|
||
_fun -> | ||
IO.write( | ||
:stderr, | ||
"Ordinal function must be 1-arity" | ||
) | ||
|
||
nil | ||
end | ||
end | ||
end |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,46 @@ | ||
defmodule Absinthe.Phase.Subscription.Prime do | ||
@moduledoc false | ||
|
||
alias Absinthe.Blueprint.Continuation | ||
alias Absinthe.Phase | ||
|
||
@spec run(any(), Keyword.t()) :: Absinthe.Phase.result_t() | ||
def run(blueprint, prime_result: prime_result) do | ||
{:ok, put_in(blueprint.execution.root_value, prime_result)} | ||
end | ||
|
||
def run(blueprint, prime_fun: prime_fun, resolution_options: options) do | ||
{:ok, prime_results} = prime_fun.(blueprint.execution) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I take it the purpose of the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Personally, I think we should have the prime function return the tuple, similar to resolutions. Otherwise you could conceivably have a There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. My thinking was mostly just to keep it consistent with return types in the other resolution callback functions. You're right that it doesn't serve much purpose at the moment, but I think at the time I was thinking we may want to allow error conditions to be propagated up like in resolutions. I'm not quite sure how that would be presented at a graphQL level though. |
||
|
||
case prime_results do | ||
[first | rest] -> | ||
blueprint = put_in(blueprint.execution.root_value, first) | ||
blueprint = maybe_add_continuations(blueprint, rest, options) | ||
{:ok, blueprint} | ||
|
||
[] -> | ||
blueprint = put_in(blueprint.result, :no_more_results) | ||
{:replace, blueprint, []} | ||
end | ||
end | ||
|
||
defp maybe_add_continuations(blueprint, [], _options), do: blueprint | ||
|
||
defp maybe_add_continuations(blueprint, remaining_results, options) do | ||
continuations = | ||
Enum.map( | ||
remaining_results, | ||
&%Continuation{ | ||
phase_input: blueprint, | ||
pipeline: [ | ||
{__MODULE__, [prime_result: &1]}, | ||
{Phase.Document.Execution.Resolution, options}, | ||
Phase.Subscription.GetOrdinal, | ||
Phase.Document.Result | ||
] | ||
} | ||
) | ||
|
||
put_in(blueprint.result, %{continuation: continuations}) | ||
end | ||
end |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -5,10 +5,44 @@ defmodule Absinthe.Phase.Subscription.Result do | |
# subscription | ||
|
||
alias Absinthe.Blueprint | ||
alias Absinthe.Blueprint.Continuation | ||
alias Absinthe.Phase | ||
|
||
@spec run(any, Keyword.t()) :: {:ok, Blueprint.t()} | ||
def run(blueprint, topic: topic) do | ||
def run(blueprint, options) do | ||
bernardd marked this conversation as resolved.
Show resolved
Hide resolved
|
||
topic = Keyword.fetch!(options, :topic) | ||
prime = Keyword.get(options, :prime) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Thoughts on a more descriptive name like There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. FWIW, I'm fine with prime, just there may be something more apt/intuitive here. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Sure. |
||
result = %{"subscribed" => topic} | ||
|
||
case prime do | ||
nil -> | ||
{:ok, put_in(blueprint.result, result)} | ||
|
||
prime_fun when is_function(prime_fun, 1) -> | ||
stash_prime(prime_fun, result, blueprint, options) | ||
|
||
val -> | ||
raise """ | ||
Invalid prime function. Must be a function of arity 1. | ||
|
||
#{inspect(val)} | ||
""" | ||
end | ||
end | ||
|
||
def stash_prime(prime_fun, base_result, blueprint, options) do | ||
continuation = %Continuation{ | ||
phase_input: blueprint, | ||
pipeline: [ | ||
{Phase.Subscription.Prime, [prime_fun: prime_fun, resolution_options: options]}, | ||
{Phase.Document.Execution.Resolution, options}, | ||
Phase.Subscription.GetOrdinal, | ||
Phase.Document.Result | ||
] | ||
} | ||
|
||
result = Map.put(base_result, :continuation, [continuation]) | ||
|
||
{:ok, put_in(blueprint.result, result)} | ||
end | ||
end |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm still working through the details here but the switch from plural to singular has me confused.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Me too, apparently :) Initially I think I had it only supporting a single continuation, but I realised potentially there may be multiple ones, so changed it but didn't do a very thorough job of it. I've fixed it up now.