Coder Social home page Coder Social logo

absinthe_phoenix's People

Contributors

adri avatar azhi avatar benwilson512 avatar bleushan avatar bruce avatar devonestes avatar dolfinus avatar g3z avatar jerel avatar jlgeering avatar katafrakt avatar kianmeng avatar maartenvanvliet avatar marocchino avatar quinnwilton avatar scrogson avatar seeekr avatar tcitworld avatar technicalcapt avatar telnicky avatar thiamsantos avatar tmw avatar vic avatar victorgaiva avatar wigny avatar woylie avatar xfumihiro avatar xtian avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

absinthe_phoenix's Issues

CRUD Generators (Schema + Resolvers + Tests)

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

Graphiql Subscriptions Integration

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

screen shot 2017-11-27 at 12 59 21 pm

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?

Can't track socket disconnect in __absinthe__:control channel

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?

Support for request context data.

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 :)

subscription not working with flutter graphql

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

(FunctionClauseError) no function clause matching in :elixir_aliases.do_concat/2

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

Superviser example code doesn't work with latest Exlir

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 👍

Does not log GraphQL errors

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.

Absinthe.Phoenix.Socket.put_options/2 overwrites options, causes loss of custom adapter.

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:

  1. Use Map.merge instead of Map.put in put_options.
  2. Manually preserve adapter as well as context in run_doc.
  3. Add a merge_options or similar.

Let me know the preferred approach and I can open a PR.

[Feauter] async WebSocket queries

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.

Malformed unsubscribe requests from `@absinthe/socket` causes GenServer crash.

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?

Subscription replay functionality

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.

Pubsub not configured Error

Sentry catches a lot of RuntimeErrors 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.

Full Exception

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

Versions

absinthe_phoenix: 1.4.3
absinthe: 1.4.13
phoenix: 1.3.4

Setup

*Absinthe.Subscription added to Application

  • Use of Absinthe.Phoenix.Socket in Socket Implementation
  • Router Forward to Absinthe.Plug (via ApolloTracing.Pipeline)

Full integration example

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!

Absinthe subscriptions OOM-ing processes

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:

  • running on GKE with .5 vcpu/2Gi per container
  • steady state with maybe 2 gql subscriptions takes upwards of 200-300Mb (I'm the only person using the application)
  • If I remove hard limits, it will consume the entire GCE node's ram before OOM killing, usually around 5Gi available.

context modified by middlewares is discarded

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 ?

Cutting new release

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

Enable join, handle_in, and handle_out hooks

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.

Controller-based mutations don't "trigger" subscriptions

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.

Phoenix's `action_fallback` is not respected

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!

isDeepEqual is not defined

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!

Duplicate subscriptions sent when same user is logged in with different browsers

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)

absinthe_phoenix 2.0.2 is blocking phoenix_html 4.0.0

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

How to handle missing data?

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?

Do not change conn.params

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).

Logger options to Phoenix.Channel

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

Link to new JS packages

@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.)

Release new version

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.

Log errors in subscription requests

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.

[Feature Request] Support Query Batching for Absinthe.Phoenix.Controller

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).

Bump decimal dependency to 2.0

Jason needs ~> 1.0 or ~> 2.0, postgrex needs ~> 1.5 or ~> 2.0 and absinthe_phoenix requires ~> 1.0 – that's a conflict :-)

Messages duplicated when using phoenix_pubsub_redis as adapter

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:
Screenshot at 2019-11-06 10-12-40

Using PG2 we have only one.
Screenshot at 2019-11-06 10-14-05

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"}

subscriptions in phoenix 14

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

Phoenix 1.4

Hello,
When will there be documentation available for phoenix 1.4?

Edge case with multiple subscriptions to same query

I noticed subscribing to the same GraphQL query twice causes some interesting behaviour. Specifically this happens:

  1. client subscribes twice to the exact same query, gets back two responses with same subscriptionId, i.e. "subscriptionId":"__absinthe__:doc:116039402"
  2. client receives two subscription:data events (as I'd expect) when changes are published
  3. unsubscribing once causes no more subscription:data events for that query, even though I have one "active" subscription

I 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?

banners_and_alerts_and_dark_websocket_terminal

Catchall `handle_info` in channel

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)?

:id type case

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

Call Phoenix Controller action from an Absinthe resolver

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?

Error "function nil.subscribe/1 is undefined or private"

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.

How to pass params?

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.

pedidos

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 ❤️

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.