sezaru

sezaru

Add "hooks" to read actions to run extra code similar to change after_transaction

This post actually have 2 questions that are kinda related one to another.

Here is what I’m trying to solve, I have some read action in my resource that, when called by our GraphQL API, should also do a :telemetry call to bump our view count metric.

My current solution was to create a manual Absynthe object that calls the read action directly and execute the telemetry as-well:

defmodule Marketplace.Markets.Property.GraphQL do
  @moduledoc false

  alias Marketplace.Markets.Property

  use Absinthe.Schema.Notation

  object :markets_property_queries do
    field :get_valid_property, :property do
      arg :id, non_null(:id)

      resolve fn _, %{id: id}, %{context: %{actor: actor}} ->
        case Property.get_valid(%{id: id}, actor: actor) do
          {:ok, property} ->
            execute(property, actor)

          {:error, %Ash.Error.Forbidden{}} ->
            {:error, :forbidden}

          {:error, _} ->
            {:error, :not_found}
        end
      end
    end
  end

  defp execute(%{id: property_id} = property, %{id: actor_id} = _actor) do
    :telemetry.execute([:property, :view], %{}, %{
      property_id: property_id,
      user_id: actor_id
    })

    {:ok, property}
  end

  defp execute(%{id: property_id} = property, _actor) do
    :telemetry.execute([:property, :view], %{}, %{
      property_id: property_id,
      user_id: nil
    })

    {:ok, property}
  end
end

This works, but it is very verbose and sits outside the resource.

It also has a problem when the GraphQL user requests to load some other reference inside the resource, it will just fail since the action itself doesn’t load that resource by default.

So, my first question is, can I move this telemetry call inside the action somehow? If it was a create, update, destroy action, I would just simply create a change with after_transaction and put the telemetry there, but this is a read action, so I’m not sure if there is anything “similar” to use for this.

My second question is, assuming the first one is possible, does AshGraphQL add some metadata to the context or somewhere else so I can know that the actions was actually called by my GraphQL API and not by calling the action directly? I just want to trigger the telemetry call when the action is called via my GraphQL API.

Marked As Solved

zachdaniel

zachdaniel

Creator of Ash

Hey! So to answer your specific question:

read :... do
  prepare fn query, _ -> 
    query
    |> Ash.Query.before_action(...)
    |> Ash.Query.after_action(...)
  end
end

You can use preparations and the hooks available to queries to do this.

However, to solve for this general kind of thing, we already broadcast telemetry hooks. You could do something like this when your application starts up.

:telemetry.attach(
  "some_id",
  [:ash, :your_api_short_name, :read], 
  &emit_your_events_when_you_see_a_matching_event/4
)

Where Next?

Popular in Questions Top

nobody
How to bind a phoenix app to a specific ip address? could not find anything about that, nowhere, unfortunately, but for me this is quite...
New
Tee
can someone please explain to me how Enum.reduce works with maps
New
minhajuddin
I have seen a lot of code which picks the first element from a list using Enum.at(0) instead of List.first. Is there a reason why people ...
New
LegitStack
I’m trying to make a websocket server in Phoenix or raw Elixir. I heard about gun, I think I could use cowboy, but since I’m not that sma...
New
RisingFromAshes
I’ve read in another post that it may be possible with a router helper - but I couldn’t find an appropriate one, and tbh, I’m still just ...
New
nobody
Hi! In PHP: $_SERVER[‘SERVER_ADDR’] - in Elixir? Searched the docs for ip address and the web, no good results. Thanks!
New
fayddelight
I tried installing elixir 1.11.2 erlang 23.3.4 via asdf in my zsh shell. Enabled the versions locally and globally. When I list them ...
New
chensan
I have a User schema with a :from_id field set to type :string: defmodule TweetBot.Repo.Migrations.CreateUsers do use Ecto.Migration ...
New
hariharasudhan94
Lets say I have map like this fetching from my database %{"_id" => #BSON.ObjectId<58eb1a7a9ad169198c3dXXXX>, "email" => ...
New
jononomo
For some reason my phoenix channels are working for me in my local dev environment, but as soon as I deploy via Docker, I get a 403 error...
New

Other popular topics Top

malloryerik
Hi, this is for people who, like me, have had some friction using .html.heex templates in VSCode. The solution seems to be, in a hyphena...
New
Darmani72
If I have a post route which an argument: post /my_post_route/:my_param1, MyController.my_post_handler How would get the post params ...
New
johnnyicon
Hi all, I’ve just started learning Elixir and Phoenix Framework, so please pardon my n00bness at this stage. I’m trying to use Postgres...
New
AngeloChecked
What learn first? Rust or Elixir Hi Elixir community! I’m here because i want learn a new language. I’m a junior developer and mainly i ...
New
alice
Hey, Just curious what are the main benefits of Elixir compared to Clojure? When is Elixir more useful than Clojure and vice versa? Th...
New
AstonJ
Please see the new poll here: Which code editor or IDE do you use? (Poll) (2022 Edition) It’s been a while since we first asked this, I...
208 31265 143
New
ashish173
I am using Ecto timestamps with postgres, I can see the timestamps() use the :naive_dateime but for my use case I wanted to store the ti...
New
dblack
I’ve got an issue with an app and I’ve no idea of how to troubleshoot it. I’m hoping someone here might have seen something similar. I p...
New
klo
Got a question about when to concat vs. prepending items to list then reversing to achieve appending. So i know lists boil down to [1 | ...
New
AstonJ
We’ve put together this wiki for Phoenix LiveView - please feel free to add any info you feel is worth including. What is Phoenix LiveV...
New

Latest on Elixir Forum

Elixir Forum

We're in Beta

About us Mission Statement