absinthe-graphql / absinthe_phoenix Goto Github PK
View Code? Open in Web Editor NEWLicense: MIT License
License: MIT License
Hello !
I'm wondering why there isn't any generators available for this library since it's been available for a moment. Is it something you've ever considered to do ? Because it would be awesome to have generators to speed up the work process with Absinthe !
Thanks
Hi there, I was trying to implement GraphQL subscriptions and did most of the stuff found in the README. However, when trying to integrate with GraphiQL, it seems to automatically connect to /graphiql/websockets
. Here's what it looks like on graphiql
And here's what I have changed in my project so far:
app/web/channels/graph_socket.ex
defmodule App.Web.GraphSocket do
use Phoenix.Socket
use Absinthe.Phoenix.Socket, schema: App.Web.Schema
transport :websocket, Phoenix.Transports.WebSocket
def connect(params, socket) do
{:ok, socket}
end
def id(_socket), do: nil
end
endpoint.ex
use Absinthe.Phoenix.Endpoint
socket "/graphiql/websocket", App.Web.GraphSocket
router.ex
scope "/graphiql" do
pipe_through :graphql
forward "/", Absinthe.Plug.GraphiQL,
schema: App.Web.Schema,
socket: App.Web.GraphSocket
end
application.ex
supervisor(Absinthe.Subscription, [App.Web.Endpoint])
What could be the issue here?
Hello there!
I have the following problem: I have customer facing site and backoffice and I would like to show online status in backoffice using graphql subscriptions. Let’s assume that I don’t have direct access to Phoenix channels and for me it’s not possible to use channels at all, I have only absinthe subscriptions. How can I detect that socket connection is closed for a specific user in absinthe subscription level.
OFC that is easy to do in plain phoenix channels but when it comes to absinthe there I couldn’t find any proper way to do this. Maybe I’m doing something wrong or it was overlooked in absinthe:control channel implementation?
Hello, this is more a question than an proper issue I guess.
I'm about to update an application of mine to use absinthe subscriptions branch and absinthe_phoenix socket, however, I'm not sure on how to implement the following:
Using the Apollo client, you can specify middle-wares that can intercept any request before it's
being sent. AFAIK it's mainly used for cases like authorization where every request is also sent with the current authentication token from the client.
So for example, the Apollo client on my app is configured to always send not only the gql document, and its variables, but also two additional things: the authentication token stored on the device, and the local-time of such device at the moment the query was made (most of the backend operations are sensitive to the user's local time).
So previously to absinthe_phoenix
my dumb Channel received "docs" something like this
example
def handle_in("doc", request = %{"query" => query}, socket) when do
variables = Map.get(request, "variables", %{})
context = request_context(%{}, request)
options = [variables: variables, context: context]
# Unlike Absinthe.Phoenix.Channel, my version just relied on Absinthe.run
# as it was the simplest way I found to execute the query.
# then, the resolve function could have access to the jwt token and localtime
# stored in the context map.
#
# IIRC this was how I was executing queries on my tests also.
{:ok, result} = Absinthe.run(query, MyGQL.Schema, options)
{:reply, {:ok, result}, socket}
end
defp request_context(context, request) do
context
|> authentication_context(request)
|> device_localtime_context(request)
end
defp authentication_context(context, %{"auth_token" => token}) do
Map.put(context, :jwt, token)
end
defp authentication_context(context, _), do: context
defp device_localtime_context(context, %{"time" => time_str}) do
Map.put(context, :local_time, Time.parse_iso!(time_str))
end
defp device_localtime_context(context, _), do: context
def resolve_function(_parent, variables, %{context: context}) do
# the resolve functions have access to the jwt token and the client device time.
end
So, now I'm wondering how I should send these "context" data so that the absinthe_phoenix's Channel can send it to my resolve functions. Any idea on how to approach this is more than welcome :)
versions:
elixir: 1.8.2
phoenix: 1.4.0
absinthe_phoenix: 1.4.0
flutter: 1.5.9-pre.223
flutter_graphql : 1.0.1-beta.7
The subscription I am testing with flutter is just simple "hello world" subscription, where I send a message and anyone who subscribe to the socket will receive the message as string.
I used the same subscription schema on node apollo-server
and flutter graphql client subscription works as expected with the simple subscription, but not absinthe_phoenix
. Here is the error on socket connected:
[info] CONNECT AppServerWeb.UserSocket
Transport: :websocket
Connect Info: %{}
Parameters: %{}
[info] Replied AppServerWeb.UserSocket :ok
[error] Ranch listener AppServerWeb.Endpoint.HTTP had connection process started with :cowboy_clear:start_link/4 at #PID<0.556.0> exit with reason: {:function_clause, [
{Phoenix.Socket.Message, :from_map!, ["{\"type\":\"connection_init\"}"], [file: 'lib/phoenix/socket/message.ex', line: 24]},
{Phoenix.Socket, :__in__, 2, [file: 'lib/phoenix/socket.ex', line: 549]}, {Phoenix.Endpoint.Cowboy2Handler, :websocket_handle, 2, [file: 'lib/phoenix/endpoint/cowboy2_handler.ex', line: 76]},
{:cowboy_websocket, :handler_call, 6, [file: '/Users/xxx/projects/xxx-server/deps/cowboy/src/cowboy_websocket.erl', line: 471]},
{:cowboy_http, :loop, 2, [file: '/Users/xxx/projects/xxx-server/deps/cowboy/src/cowboy_http.erl', line: 233]},
{:proc_lib, :init_p_do_apply, 3, [file: 'proc_lib.erl', line: 249]}]}
# .. same error goes on because client keeps trying to reconnect when it fails to connect to the socket
here is the lib/app_server_web/endpoint.ex
defmodule AppServerWeb.Endpoint do
use Phoenix.Endpoint, otp_app: :app_server
use Absinthe.Phoenix.Endpoint
def init(_, config), do: {:ok, config}
socket "/sub", AppServerWeb.UserSocket,
websocket: true,
longpoll: false
plug Plug.Static,
at: "/",
from: :app_server,
gzip: false,
only: ~w(css fonts images js favicon.ico robots.txt)
if code_reloading? do
socket "/phoenix/live_reload/socket", Phoenix.LiveReloader.Socket
plug Phoenix.LiveReloader
plug Phoenix.CodeReloader
end
plug Plug.RequestId
plug Plug.Logger
plug Plug.Parsers,
parsers: [:urlencoded, :multipart, :json, Absinthe.Plug.Parser],
pass: ["*/*"],
json_decoder: Phoenix.json_library()
...
the config/dev.ex
...
config :app_server, AppServerWeb.Endpoint,
url: [host: "localhost"],
secret_key_base: "+ZcrZpYSVrwth8Ao0Tklp1sgjNYx36IzsktioslL+JmHYWltXfS46W5fFcDZlStU",
render_errors: [view: AppServerWeb.ErrorView, accepts: ~w(html json)],
pubsub: [name: AppServer.PubSub,
adapter: Phoenix.PubSub.PG2]
config :app_server, AppServerWeb.Endpoint,
pubsub: [
adapter: Phoenix.PubSub.Redis,
node_name: "app_server_pubsub"
]
...
the lib/app_server_web/user_socket.ex
defmodule AppServerWeb.UserSocket do
use Phoenix.Socket
use Absinthe.Phoenix.Socket, schema: AppServerWeb.Schema
alias AppServer.Accounts
# def connect(_param, socket), do: {:ok, socket}
def connect(%{"tk" => "Bearer " <> token, "rftk" => refresh_token}, socket) do
case verify_and_decode_token(token, refresh_token) do
{:ok, user_id, _, _} ->
{:ok, Absinthe.Phoenix.Socket.put_options(socket, context: %{user_id: user_id})}
{:ok, user_id} ->
{:ok, Absinthe.Phoenix.Socket.put_options(socket, context: %{user_id: user_id})}
_ ->
:error
end
end
def connect(_params, socket), do: {:ok, socket}
def id(_socket), do: nil
...
end
With this config {Absinthe.Subscription, [MyAppWeb.Endpoint]}
. I'm getting
(EXIT) an exception was raised:
** (FunctionClauseError) no function clause matching in :elixir_aliases.do_concat/2
(elixir 1.10.1) src/elixir_aliases.erl:120: :elixir_aliases.do_concat([[MyAppWeb.Endpoint], :Registry], "Elixir")
(elixir 1.10.1) src/elixir_aliases.erl:108: :elixir_aliases.concat/1
(absinthe 1.5.0) lib/absinthe/subscription/supervisor.ex:11: Absinthe.Subscription.Supervisor.init/1
Hi,
The README currently contains the instructions:
In your application supervisor add a line AFTER your existing endpoint supervision
line:
[
# other children ...
supervisor(MyAppWeb.Endpoint, []), # this line should already exist
supervisor(Absinthe.Subscription, [MyAppWeb.Endpoint]), # add this line
# other children ...
]
Where MyAppWeb.Endpoint
is the name of your application's phoenix endpoint.
I had to modify this slightly to work with Elixir 1.7:
[
# other children ...
MyAppWeb.Endpoint, # this line should already exist
# add these lines:
%{
id: Absinthe.Subscription,
start: {Absinthe.Subscription, :start_link, [MyApp.Endpoint]},
type: :supervisor
}
# other children ...
]
This got it to work for me-- is this the correct way to do it now, and should the docs be updated to reflect this?
Thanks 👍
For context on this, see https://elixirforum.com/t/what-does-this-absinthe-phoenix-debugging-output-mean/8315. In short, I'm getting debugging info that indicates a query is hitting my schema, but I get a network error in my Apollo client, and no debugging info on the console.
I tweaked the channel resolver code to look like so:
Absinthe.Logger.log_run(:debug, {
query,
config.schema,
[],
opts,
})
result = Absinthe.run(query, config.schema, opts)
Absinthe.Logger.log_run(:debug, {
result
})
This is bad code and causes a crash. However, the crash reveals what's going on here:
** (FunctionClauseError) no function clause matching in Absinthe.Logger.log_run/2
(absinthe) lib/absinthe/logger.ex:54: Absinthe.Logger.log_run(:debug, {{:ok, %{errors: [%{locations: [%{column: 0, line: 5}], message: "Cannot query field \"id\" on type \"User\"."}]}}})
(absinthe_phoenix) lib/absinthe/phoenix/channel.ex:50: Absinthe.Phoenix.Channel.handle_in/3
(phoenix) lib/phoenix/channel/server.ex:244: anonymous fn/4 in Phoenix.Channel.Server.handle_info/2
(app) lib/app_web/endpoint.ex:1: AppWeb.Endpoint.instrument/4
(stdlib) gen_server.erl:616: :gen_server.try_dispatch/4
(stdlib) gen_server.erl:686: :gen_server.handle_msg/6
(stdlib) proc_lib.erl:247: :proc_lib.init_p_do_apply/3
So, in short, there's an error in my schema, and I failed to export the id
field on my User
type. But it'd be nice if that error had been reported to me somehow. It also doesn't appear to make it back to my websocket client.
Custom adapters & Absinthe.Phoenix.Channels do not work.
When using a custom adapter only the first request uses the adapter, subsequent request do not.
This happens due to the use of Absinthe.Phoenix.Socket.put_options/2
inside Absinthe.Phoenix.Channels.run_doc/4
- Absinthe.Phoenix.Socket.put_options/2
overwrites the entire :opts
field.
Some simple solutions might be:
Let me know the preferred approach and I can open a PR.
For various reasons we run all our queries (and mutations) via WebSocket and found that all requests are handled sequentially in Absinthe.Phoenix.Channel
. It would be great if we could execute queries asynchronously. As part of this we could also add some error handling for better stability, because when a query/mutation raises/throws an exception that is not caught in the resolver, the channel process simply dies. In the frontend we use apollo-socket-link which then rejoins the channel and restarts all pending queries/mutations (including the one that caused the exception). If the error persists, we end up in an infinite loop.
I have sketched out a proof of concept implementation here: https://gist.github.com/mpoeter/d18885caa3b46d47f031f6d913c93208
It spawns a monitored process for every single request. Requests that contain mutations are executed sequentially (i.e., we wait for the task to finish), other queries simply run in the background. Since the processes are not linked, the channel process does not die in case of an exception. But since it is monitored we can send a proper error reply (similar to what happens if a HTTP request dies due to an exception).
I would like to use this issue to discuss pros/cons of this approach and if/how this could be implemented as part of Absinthe.Phoenix.Channel
.
Hi there, I'm using the @absinthe/socket
node package to talk to my subscriptions server side, but upon cleanup, (I'm not sure exactly how, I think its navigating away before requests are finished (or possibly evening starting)) its sending an unsubscribe request for the control channel without a subscription id, which crashes out because handle_in
only handles "unsubscribe"
for a specific connection id, despite the fact it could handle the socket cleanup without one.
Would you welcome a PR to fix this?
I've been working on enabling client functionality to specify a replayId
when they subscribe, such that any messages that occurred during downtime can be delivered. There are a couple of places where I'm hooking into / reimplementing Absinthe functionality, and am interested in exploring if there is appetite for this functionality in the framework itself. Since I imagine there will be better insights from the authors on how to go about implementing it, here is a gist with the relevant pieces from my code, and we can figure out the best way to adapt it if desired.
Sentry catches a lot of RuntimeError
s saying Pubsub not configured! Subscriptions require a configured pubsub module.
.
I could not find out in which cases it is failing, i just see around 600 errors over a timespan of 30 days. I've never experienced the error myself and it only occurs in a low percentage of all the subscriptions.
Elixir.RuntimeError: Pubsub not configured!
Subscriptions require a configured pubsub module.
File "lib/absinthe/phase/subscription/subscribe_self.ex", line 105, in Absinthe.Phase.Subscription.SubscribeSelf.ensure_pubsub!/1
File "lib/absinthe/phase/subscription/subscribe_self.ex", line 20, in Absinthe.Phase.Subscription.SubscribeSelf.do_subscription/3
File "lib/absinthe/pipeline.ex", line 274, in Absinthe.Pipeline.run_phase/3
File "lib/absinthe/phoenix/channel.ex", line 98, in Absinthe.Phoenix.Channel.run/4
File "lib/absinthe/phoenix/channel.ex", line 59, in Absinthe.Phoenix.Channel.handle_in/3
File "lib/phoenix/channel/server.ex", line 244, in anonymous fn/4 in Phoenix.Channel.Server.handle_info/2
File "lib/acme_api/endpoint.ex", line 1, in AcmeApi.Endpoint.instrument/4
File "gen_server.erl", line 616, in :gen_server.try_dispatch/4
File "gen_server.erl", line 686, in :gen_server.handle_msg/6
File "proc_lib.erl", line 247, in :proc_lib.init_p_do_apply/3
Module "Elixir.Phoenix.Channel.Server", in Phoenix.Channel.Server.init/1
absinthe_phoenix: 1.4.3
absinthe: 1.4.13
phoenix: 1.3.4
*Absinthe.Subscription
added to Application
Absinthe.Phoenix.Socket
in Socket ImplementationAbsinthe.Plug
(via ApolloTracing.Pipeline
)Hey there, really excited to use the new subscription feature!
However after few hours still couldn't make it work, I was wondering if there's an example fully integrating absinthe_phoenix
with absinthe-socket
.
FWIW I've posted my issue here.
Thanks!
Is Phoenix 1.6 compatible with this?
It's missing in hex package and in github release tar.
I've been working on a personal project as a way to experiment with graphql on elixir, and came across some odd behavior. If I initiate maybe 20-30 or so subscribe/resubscribes, it will kill the node due to an OOM error. I've used vanilla phoenix pubsub extensively for almost 3 years now, and never had any similar issue (although you do sometimes need to manually gc them), and just to confirm, I disabled graphql subscriptions and left the few channels being used for presence and similar use cases up, and had no memory usage issues.
My hesitant guess is something about my graphql schema is complex enough to require a massive footprint, but it still seems very suspicious.
Here's some specs:
i built a simple middleware to modify the context like so
field :validate_code, type: :session do
arg :phone, non_null(:string)
arg :code, non_null(:string)
resolve handle_errors(&DoStuff.UserResolver.validate_code/2)
# Update context with current user
middleware fn res, _ ->
case res.value.user do
nil -> res
_ -> %{res | context: Map.put(res.context, :current_user, res.value.user)}
end
end
end
but context modification is discarded after execution
I also tried inspecting absinthe_phoenix internals by substituting https://github.com/absinthe-graphql/absinthe_phoenix/blob/master/lib/absinthe/phoenix/channel.ex#L49 with
pipeline = config.schema
|> Absinthe.Pipeline.for_document(opts)
result = case Absinthe.Pipeline.run(query, pipeline) do
{:ok, data, _phases} ->
data |> IO.inspect
{:ok, data.result}
{:error, msg, _phases} ->
{:error, msg}
end
data
has only a context
key which is inside resolution
and contains the original context.
Is it possible to retrieve the new context from the blueprint and assign it to the socket ?
Hi,
Is there any chance you can cut a new release to include the Update phoenix_html to 2.11
changes, also it looks like the previous point releases are not pushed to GitHub as the last release is showing as 1.4.0.
Thanks
Sam
While trying to upgrade a project to use Absinthe 1.5, I ran into issues with the @put
directive. When using :absinthe_controller
to decorate controller actions with a GQL query that uses @put
, my queries are now failing with the error "Unknown directive 'put'."
I replicated this on master with a failing test here: https://github.com/QuinnWilton/absinthe_phoenix/blob/quinn%2Fput-tests/test/absinthe/phoenix/controller_test.exs#L132
I'd like to be able to gather some data about the messages being sent over a subscription as well as the socket over which it is being sent. Right now it doesn't appear that there is a clean way to integrate with the auto-generated channels and topics.
I can expand on this as much as you want so I'll just put down the basic info for now.
If I have a mutation in a phoenix controller (in a @graphql
module attribute), like this:
@graphql """
mutation UpdateMenuItem($id: ID!, $description: String!) {
update_menu_item(id: $id, input: {description: $description}) {
errors { key message }
menu_item {
name
description
}
}
}
"""
def update(conn, %{data: %{update_menu_item: %{errors: nil, menu_item: menu_item}}}) do
# ... controller update logic here, conn |> redirect or whatever
end
And if this mutation is meant to trigger a subscription, for example if my schema.ex
is like this:
subscription do
field :updated_menu_item, :menu_item do
config(fn _args, _info ->
{:ok, topic: "*"}
end)
trigger(:update_menu_item,
topic: fn
%{menu_item: menu_item} -> ["*"]
_ -> []
end
)
resolve(fn %{menu_item: menu_item}, _, _ ->
{:ok, menu_item}
end)
end
# more subscriptions,etc.
end
then the subscription is not triggered. This might have something to do with how the Absinthe.Phoenix SDL is processed differently to normal Graphql SDL.
Using the master branch of absinthe_phoenix
's Controller to support REST interface calls to a GraphQL backend, the standard Phoenix.Controller action_fallback
feature is not supported.
defmodule MyProject.AwesomeController do
use Phoenix.Controller
use Absinthe.Phoenix.Controller, schema: Graph.Schema
action_fallback Api.Web.JsonGraphFallbackResponse
# ...
end
It doesn't error, but the call function in the specified fallback module never executes.
Ideally the action_fallback
would be respected so I can, in one place, handle unexpected GraphQL errors.
Love the library! Such an awesome time saver!
Just opening again as a place to track the progress of calling GraphQL on the server via Absinthe.
Some context:
Hello! I'm trying to set up a simple example app using absinthe_phoenix, but I can't get the subscriptions to work. When I run a subscription query in GraphiQL with no WS URL configured, I get this (as expected): "Subscriptions cannot be run over HTTP. Please configure a websocket connection"
When I set the WS URL to ws://localhost:4000/graphiql
or ws://localhost:4000/socket
(or use the simple GraphiQL interface) and run my query, I do not see anything in the network panel of my browser and the only thing that shows up in the query results pane is isDeepEqual is not defined
. I'm sure there is something simple that I'm missing, but I followed the guides and can't get it to work. Any help would be appreciated!
Attempting to upgrade phoenix and getting this error:
Failed to use "phoenix" (versions 1.6.0 to 1.6.9) because
absinthe_phoenix (version 2.0.2) requires ~> 1.5
We are using subscriptions a fair bit to communicate the state of messages between client and server. At times, the same user will log in via two different browsers.
When this happens, we have the same subscription sent twice to BOTH browsers. They also have the same subscription id.
We have the user object in the absinthe context. We also are using publish_data to publish the subscriptions from the code base (and not via the schema)
Tried upgrading phoniex_html
to version 4.0.0
in a project that has absinthe_phoenix
version 2.0.2
. Running mix deps.get
shows:
$ mix deps.get
Resolving Hex dependencies...
Resolution completed in 0.042s
Because your app depends on absinthe_phoenix 2.0.2 which depends on phoenix_html ~> 2.13 or ~> 3.0, phoenix_html ~> 2.13 or ~> 3.0 is required.
So, because your app depends on phoenix_html 4.0.0, version solving failed.
** (Mix) Hex dependency resolution failed
Using Phoenix alone I handled missing data ("not found") by just using Ecto's !
functions (one!
etc.). These raise an Ecto.NoResultsError
, which Phoenix can handle via a Plug.Exception
implementation to show a 404 page.
I'm now changing my app to use Absinthe.Phoenix.Controller
and would like to know what's the best way to handle these cases.
My current approach looks like this, but it's quite repetitive. I'm using the new action_fallback
macro from Phoenix 1.3.
@graphql """
query ($id: ID!) {
file(id: $id) @put {
owner
}
}
"""
def show(conn, %{data: %{file: nil}}), do: {:error, :not_found}
def show(conn, %{data: data}) do
render(conn, "show.html", file: data.file)
end
Is there a better/ suggested way to do this properly?
After upgrading absinthe to v1.6.0 I get this error:
Failed to use "absinthe" (version 1.6.0) because
absinthe_phoenix (version 2.0.0) requires ~> 1.5.0
Currently Absinthe.Phoenix.Controller
changes the %Plug.Conn{params: %{}}
field. This makes it hard to get the original params and breaks the Phoenix.HTML.FormData
implementation.
I like having the GraphQL response as second parameter for the controller functions, but there should be a way to access the original params, even better they shouldn't be changed at all.
Maybe it's possible to store the GraphQL response in the conn and then override the action
function to still have it as a second parameter, possibly allowing to pattern match agains the original as a third argument (but it doesn't seem possible using action/2 and action/3 at the same time).
It would be great if it was possible to set these logging options for Phoenix.Channel
Right now, since use Phoenix.Channel
is done inside Absinthe.Phoenix.Channel there is no way of setting them from what I can see.
A walkaround that feels a bit hacky is to do something like this in the socket connect callback:
def connect(params, socket) do
socket = %{
socket
| private:
Map.merge(socket.private, %{
log_join: false,
log_handle_in: false
})
}
{:ok, socket}
end
@tlvenn's team is working on a new set of Absinthe-supporting JS packages that look like they'll be sigificantly more solid than what we've put out so far.
This is just a reminder to add links to them when they become available. (We can discuss them becoming "official" later, as well.)
Hi.
What do you think about releasing new version of absinthe_phoenix? The last one was released on May 2020, and a bunch of new bugfixes/features has ben added into the master branch since when.
I ran into an issue today with subscriptions where the client wasn't receiving a valid response from the socket, but the server did not output any errors to the log. On suggestion from benwilson512, I added some logging to the query and reply in channel.ex and that was able to tell me what the error was.
It would be great if the library logged out these errors by default.
As discussed on Slack, Absinthe.Phoenix.Controller
should support query batching. This allows, for example, running both a query and a mutation for one action, which is needed when a mutation fails (as changesets need the original data) or other information should be shown on the page.
Also, Absinthe.run
should support the „special syntax“ and follow @action(mode: INTERNAL)
.
Jason needs ~> 1.0
or ~> 2.0
, postgrex needs ~> 1.5
or ~> 2.0
and absinthe_phoenix requires ~> 1.0
– that's a conflict :-)
Whenever I published a message on a subscription it always publishes two messages instead of one. It only happens when the phoenix_pubsub_redis
adapter is used for the pubsub. If I use the PG2 adapter the errors stops from happening.
Using Redis as backend we have two messages been published:
The expected behaviour would be only one message been published.
I don't know if it is a issue with absinthe or with the redis adapter, but is a really strange behaviour.
I created a bootstrap phoenix project with the minimal to simulate the behaviour.
Versions:
Elixir version: 1.9.1
OTP version: 21.1.1
Deps:
{:phoenix, "~> 1.4.9"},
{:phoenix_pubsub, "~> 1.1"},
{:gettext, "~> 0.11"},
{:jason, "~> 1.0"},
{:plug_cowboy, "~> 2.0"},
{:absinthe_plug, "~> 1.4"},
{:absinthe_phoenix, "~> 1.4"},
{:phoenix_pubsub_redis, "~> 2.1"}
Hi, currently trying to setup subscriptions in phoenix 1.4 and currently having difficulty getting it setup
I setup a subcription
object :order_cycle_subscriptions do
field :order_cycle_created, :order_cycle do
arg :restaurant_id, :id |> non_null()
config fn args, context ->
IO.puts("=====>> configured topi")
IO.inspect(args)
IO.inspect(context)
{:ok, topic: args.restaurant_id}
end
trigger :create_order_cycle, topic: fn order_cycle ->
IO.puts("=====>> triggerring create order cyle")
IO.inspect(order_cycle)
order_cycle.restaurant_id
end
end
end
now when I run the :create_order_cycle, I can confirm that the trigger callback runs but the config callback does not. at what point in the lifecycle does that config run? I’m currently not getting any values when I subscribe to this channel from graphiql but I do see the subscriptoon listen in the graphiql schema viewer (edited)
in my router I have this set
scope "/api/v1/gql" do
pipe_through :api
forward "/graphiql", Absinthe.Plug.GraphiQL,
schema: BlinqWeb.Schema,
socket: BlinqWeb.GqlSubscriptionSocket,
interface: :playground,
json_codec: Jason
forward "/", Absinthe.Plug,
schema: BlinqWeb.Schema,
socket: BlinqWeb.GqlSubscriptionSocket,
json_codec: Jason
end
currently ot even trying to enforce authentication so
defmodule BlinqWeb.GqlSubscriptionSocket do
use Phoenix.Socket
use Absinthe.Phoenix.Socket,
schema: BlinqWeb.Schema
#transport :websocket, Phoenix.Transports.WebSocket
def connect(params, socket) do
#current_user = current_user(params)
#socket = Absinthe.Phoenix.Socket.put_opts(socket, context: %{
# current_user: current_user
#})
{:ok, socket}
end
defp current_user(%{"user_id" => id}) do
#MyApp.Repo.get(User, id)
end
def id(_socket), do: nil
end
I’m using phoenix 1.4 with absinthe 1.4.16
in the console I see a 400 hit /api/v1/gql/graphiql/websocket
on subscription from graphiql.
I did make some minor deviations based on #52. in addition I added
socket "/api/v1/gql/", BlinqWeb.GqlSubscriptionSocket,
websocket: true,
longpoll: false
to my Endpoint file and eschewed the transport :websocket, Phoenix.Transports.WebSocket
line in my socket file as 1.4 deprecates it and I have a feeling that might have something to do with the websocket stuff not working
I'm getting no other errors popping up when I start up the server
Hello,
When will there be documentation available for phoenix 1.4?
I noticed subscribing to the same GraphQL query twice causes some interesting behaviour. Specifically this happens:
"subscriptionId":"__absinthe__:doc:116039402"
subscription:data
events (as I'd expect) when changes are publishedsubscription:data
events for that query, even though I have one "active" subscriptionI was wondering what the expected behaviour is? I can try to take a stab at fixing it
Personally, I think it would make sense to require unsubscribing twice (i.e. each doc
event for a single query should be paired up with a unsubscribe
event)
Thoughts?
Would it be reasonable to add a catchall handle_info
function to Absinthe.Phoenix.Channel
? ie:
def handle_info(_, socket), do: {:noreply, socket}
The reason I ask is that we have a "waiter" type module which waits on a specific message with a timeout. BEAM being what it is, of course, it's nigh on impossible to completely guarantee that that message won't arrive after the timeout. In the current form, if this happens the channel will crash with a function clause error. A catchall would solve that, but I understand if it seems maybe overly broad. If you don't like that idea, would it be reasonable to rejig it so that the user can specify their own handle_info overrides (just like is currently done for Phoenix.Channel
)?
Hello,
I'm not sure this is even an issue but I noticed that :id
types are passed as typed by the query (I'm using uuids) to trigger function.
I noticed Ecto always returns a lowercase version of the uuid
no big deal but I had trouble finding the issue because in queries it's transparent
this is how i fixed the problem
topic fn args ->
String.downcase(args.group_id)
end
I don't know if you want to mention this in the readme or fix it automagically
Hi,
I have a doubt about how to go about a problem. I think this repo is the closest I can get to an answer.
Here is my problem statement:
I have an existing Phoenix Project (Pure API project without UI). I have a controller and an action which is called whenever a POST request is received with certain parameters.
There are around 5 to 6 plugs which get called before it comes to the action method in the controller.
Now, I currently use POSTMAN to make POST calls to this API. However, in POSTMAN, there is no validation for the parameters which are passed as a body to this POST request.
This is where GraphQL comes in. I am thinking about using GraphIQL as an alternative to POSTMAN so that I can get introspection and validation.
Now, I have setup Absinthe and got to the point where the resolver method gets called for the GraphIQL request. Now, the question is how do I exercise the Controller Action from this resolver. Note that my GraphIQL request should also go through the 5 6 plugs and then go to the controller action. The controller action will return back the output to the GraphIQL web interface.
Can anyone please point me to something which help me achieve this?
I'm trying to use this package in my project, but I can't run any subscription, even the example.
I receive this error message:
[debug] ABSINTHE schema=MyApp.Schema variables=%{}
---
subscription {
commentAdded(repoFullName: "foo") {
title
}
}
---
[info] Sent 500 in 134ms
[error] #PID<0.518.0> running MyApp.Endpoint terminated
Server: localhost:4000 (http)
Request: POST /api
** (exit) an exception was raised:
** (UndefinedFunctionError) function nil.subscribe/1 is undefined or private
nil.subscribe("__absinthe__:doc:52740976")
(absinthe_plug) lib/absinthe/plug.ex:218: Absinthe.Plug.subscribe/3
(phoenix) lib/phoenix/router/route.ex:161: Phoenix.Router.Route.forward/4
(phoenix) lib/phoenix/router.ex:278: Phoenix.Router.__call__/1
(myapp) lib/myapp/endpoint.ex:1: MyApp.Endpoint.plug_builder_call/2
(myapp) lib/plug/debugger.ex:99: MyApp.Endpoint."call (overridable 3)"/2
(myapp) lib/myapp/endpoint.ex:1: MyApp.Endpoint.call/2
(plug) lib/plug/adapters/cowboy/handler.ex:15: Plug.Adapters.Cowboy.Handler.upgrade/4
(cowboy) /Users/macabeus/Dropbox (BEPiD)/myapp/server/deps/cowboy/src/cowboy_protocol.erl:442: :cowboy_protocol.execute/4
My mix deps:
[
{:phoenix, "~> 1.3.0"},
{:phoenix_pubsub, "~> 1.0"},
{:phoenix_ecto, "~> 3.2"},
{:ecto_enum, ">= 0.0.0"},
{:postgrex, ">= 0.0.0"},
{:phoenix_html, "~> 2.10"},
{:phoenix_live_reload, "~> 1.0", only: :dev},
{:gettext, "~> 0.11"},
{:cowboy, "~> 1.0"},
{:absinthe, "~> 1.4.0-rc.3"},
{:absinthe_plug, "~> 1.4.0-rc.1"},
{:absinthe_ecto, "~> 0.1.2"},
{:absinthe_phoenix, github: "absinthe-graphql/absinthe_phoenix"},
{:poison, "~> 3.1"},
{:faker, "~> 0.9.0"},
{:timex, "~> 3.1"}
]
I really don't know the reason of this error.
Hello again, and sorry to bother so much, but couldn't find a solution by looking at the docs and diving into the code.
I'm trying to put the current user in the context but the params
in def connect(params, socket)
doesn't give me the arguments as I was expecting.
Instead, it returns the params in the query string, and I don't see my argument anywhere in that function.
pry(2)> params
%{"vsn" => "2.0.0"}
pry(3)> socket
%Phoenix.Socket{assigns: %{}, channel: nil, channel_pid: nil,
endpoint: Api.Endpoint, handler: Api.Channels.OrderSocket, id: nil,
join_ref: nil, joined: false, private: %{}, pubsub_server: Api.PubSub,
ref: nil, serializer: Phoenix.Transports.V2.WebSocketSerializer,
topic: nil, transport: Phoenix.Transports.WebSocket,
transport_name: :websocket, transport_pid: #PID<0.780.0>, vsn: "2.0.0"}
This is the related code:
JS
const operation = `subscription motoboyOrders($authToken: String!) {
motoboyOrders(authToken: $authToken) {
name
}
}`
const absintheSocket = AbsintheSocket.create(
new PhoenixSocket("ws://localhost:4001/socket")
);
const notifier = AbsintheSocket.send(absintheSocket, {
operation,
variables: {authToken: "my-auth-token"}
});
Server:
subscription do
field :motoboy_orders, :order do
arg :auth_token, non_null(:string)
config fn args, _ ->
{:ok, topic: args.auth_token}
end
trigger :create_order, topic: fn order ->
order.motoboy.auth_token
end
end
end
def connect(params, socket) do
require IEx
IEx.pry
socket = Absinthe.Phoenix.Socket.put_opts(socket, context: %{
current_motoboy: current_motoboy(params)
})
{:ok, socket}
end
defp current_motoboy(%{"auth_token" => auth_token}) do
from(m in Motoboy, where: [auth_token: ^auth_token])
|> first
|> Repo.one
end
What am I missing here?
Thanks again ❤️
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.