{@thread.name}

Jan Ulbrich
2023-01-27

Jan Ulbrich:

I have a read action (and JSON API route) that takes an alternative identifer :extid instead of looking for the :id column. Now I would like the same route as an alternative for updates, too.

The update understands the argument like the read does, but I don’t see how I can modifiy the query to read the correct record.

A change can have an optional where which takes a list of functions, but I don’t get what these functions should look like…

ZachDaniel:

Like you want to update the record with the given :extid ?

ZachDaniel:

You would do something like this:

patch :update_action do
  route "/:extid"
end

ZachDaniel:

And then it would look up with the extid attribute

Jan Ulbrich:

Oh, then I had it right. I got this error message and though I need to fix the lookup:

PATCH /api/eventhub/events/alarms/R202201010001 ** (exit) an exception was raised:     ** (RuntimeError) Haven't figured out more complex route parameters yet.

ZachDaniel:

ZachDaniel:

what

ZachDaniel:

lemme take a look 😆

ZachDaniel:

oh, wow

ZachDaniel:

thats an error in the json schema building

ZachDaniel:

    unless properties == [] or properties == ["id"] do
      raise "Haven't figured out more complex route parameters yet."
    end

ZachDaniel:

But of course that is being used to validate the input and so is causing a problem

ZachDaniel:

I’ll fix it

Jan Ulbrich:

Wow, you’re amazing! 😄

Jan Ulbrich:

Here’s a more complete part of my code:

json_api do
  type "event"

  routes do
    base("/events")

    get(:read_alarm, route: "/alarms/:extid")
    get(:read)

    patch(:update_alarm, route: "/alarms/:extid")
    patch(:update)
  end
end

update :update_alarm do
  argument :extid, :string, allow_nil?: false

  change fn changeset, context ->
    patch_changeset_applying_actor_filters(changeset, context)
  end
end

update :update do
  change fn changeset, context ->
    patch_changeset_applying_actor_filters(changeset, context)
  end
end

Compiler:

ZachDaniel:

Hey <@1066359540003643526> would you mind trying ash_json_api’s main branch?

Jan Ulbrich:

Sure, gimme a couple of minutes…

Jan Ulbrich:

At least I’m getting nearer! 🙂

First I ran into error "InvalidBody", "detail": "Required properties are missing: [\"extid\"], at [\"data\", \"attributes\"]." which tells me, that the extid isn’t read from the path.

I added it to the body to get further and now I get FrameworkError", "detail": "Returned when an unexpected error in the framework has occured." without any exception. In the log I see that a query was executed on the database and the query looks good!

Jan Ulbrich:

Ha, I guess I know the problem: The extid is not unique without a second condition which I need to add. Can I add an additional where clause?

ZachDaniel:

You can only have one filter on a read action

ZachDaniel:

but you can do things like filter expr(foo == ^arg(:foo) and bar == ^arg(:bar))

Jan Ulbrich:

In the read I already have that filter , but an update doesn’t offer that… 😇

Jan Ulbrich:

I guessed, that the where of the change could help me, but I couldn’t figure out how.

Jan Ulbrich:

The read looks like this:

read :read_alarm do
  argument :extid, :string, allow_nil?: false

  filter expr(kind == "ALARM" and subkind == "NEW" and extid == ^arg(:extid))

  prepare fn query, context ->
    Ash.Query.after_action(query, fn _query, events ->
      {:ok, patch_results_applying_actor_filters(events, context)}
    end)
  end
end

The update looks like this (and obviously the additional where clause is missing to make the lookup unique):

update :update_alarm do
  argument :extid, :string, allow_nil?: false

  change fn changeset, context ->
    patch_changeset_applying_actor_filters(changeset, context)
  end
end

ZachDaniel:

ah, the where of the change is for something else 🙂

ZachDaniel:

That is for conditionally running changes

ZachDaniel:

Can I see your patch route with the additional field?

Jan Ulbrich:

Sure, here: patch(:update_alarm, route: "/alarms/:extid")

The whole rout block is further above…

ZachDaniel:

I think you need the other field in there right?

ZachDaniel:

You mentioned its not unique on :extid

Jan Ulbrich:

See the read: The lookup per extid is unique as long as I have the additional clauses.

ZachDaniel:

Ah, I see

Jan Ulbrich:

Database model isn’t mine, so not the nicest stuff, but legacy I need to live with… 😇

ZachDaniel:

Not a problem 🙂

ZachDaniel:

So this is a feature we have in ash_graphql that hasn’t been added to ash_json_api yet

ZachDaniel:

It will not be hard to add, but what you basically need to be able to do is customize the read action used to look up the thing you want to update

ZachDaniel:

something like this:

patch "/alarms/:extid" do
  read_action :read_alarm
end

ZachDaniel:

Its an easy change, and I will make it later tonight

ZachDaniel:

keeping up with all the extensions can be tough sometimes so features are generally added on an as-needed basis and you’re the first person to ask for this one for ash_json_api, but its a very important feature 🙂

Jan Ulbrich:

You’re brilliant! 🤩 But please don’t go crazy: I can live without that for the moment.

And that I need to put the extid in the patch body is related to the same, right?

Jan Ulbrich:

I wanted to look into the Ash GraphQL next anyways… 🙂 Thanks a lot for your help, good night! 😴

ZachDaniel:

Yeah, exactly. The extid should be fine to be in the route 😄 once its fixed

ZachDaniel:

Can you try main now? Should work the way you want it to 🙂

ZachDaniel:

But you’ll add read_action : read_alarm

ZachDaniel:

I’ve added it to patch and delete

ZachDaniel:

Will likely need to add it to others as well, but this gets the initial support going

Jan Ulbrich:

Wow, will do!

Jan Ulbrich:

The read_action does the trick now! 👏 So I can ship that tomorrow. Will go to sleep now happily… 😄 🙃 😴 Thank you very much! 🤩

ZachDaniel:

🥳 glad I could help!