Update Route W/ Arguments

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…
20 Replies
ZachDaniel
ZachDaniel3y ago
Like you want to update the record with the given :extid? You would do something like this:
patch :update_action do
route "/:extid"
end
patch :update_action do
route "/:extid"
end
And then it would look up with the extid attribute
Jan Ulbrich
Jan UlbrichOP3y ago
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
ZachDaniel3y ago
... what lemme take a look 😆 oh, wow thats an error in the json schema building
unless properties == [] or properties == ["id"] do
raise "Haven't figured out more complex route parameters yet."
end
unless properties == [] or properties == ["id"] do
raise "Haven't figured out more complex route parameters yet."
end
But of course that is being used to validate the input and so is causing a problem I'll fix it
Jan Ulbrich
Jan UlbrichOP3y ago
Wow, you’re amazing! 😄 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
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
Compiler3y ago
Critical error:
Unable to find a codeblock to format!
Requested by: Jan Ulbrich#0386
ZachDaniel
ZachDaniel3y ago
Hey @Jan Ulbrich would you mind trying ash_json_api's main branch?
Jan Ulbrich
Jan UlbrichOP3y ago
Sure, gimme a couple of minutes… 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! 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
ZachDaniel3y ago
You can only have one filter on a read action but you can do things like filter expr(foo == ^arg(:foo) and bar == ^arg(:bar))
Jan Ulbrich
Jan UlbrichOP3y ago
In the read I already have that filter, but an update doesn’t offer that… 😇 I guessed, that the where of the change could help me, but I couldn't figure out how. 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
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
update :update_alarm do
argument :extid, :string, allow_nil?: false

change fn changeset, context ->
patch_changeset_applying_actor_filters(changeset, context)
end
end
ZachDaniel
ZachDaniel3y ago
ah, the where of the change is for something else 🙂 That is for conditionally running changes Can I see your patch route with the additional field?
Jan Ulbrich
Jan UlbrichOP3y ago
Sure, here: patch(:update_alarm, route: "/alarms/:extid") The whole rout block is further above…
ZachDaniel
ZachDaniel3y ago
I think you need the other field in there right? You mentioned its not unique on :extid
Jan Ulbrich
Jan UlbrichOP3y ago
See the read: The lookup per extid is unique as long as I have the additional clauses.
ZachDaniel
ZachDaniel3y ago
Ah, I see
Jan Ulbrich
Jan UlbrichOP3y ago
Database model isn’t mine, so not the nicest stuff, but legacy I need to live with… 😇
ZachDaniel
ZachDaniel3y ago
Not a problem 🙂 So this is a feature we have in ash_graphql that hasn't been added to ash_json_api yet 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 something like this:
patch "/alarms/:extid" do
read_action :read_alarm
end
patch "/alarms/:extid" do
read_action :read_alarm
end
Its an easy change, and I will make it later tonight 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
Jan UlbrichOP3y ago
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? I wanted to look into the Ash GraphQL next anyways… 🙂 Thanks a lot for your help, good night! 😴
ZachDaniel
ZachDaniel3y ago
Yeah, exactly. The extid should be fine to be in the route 😄 once its fixed Can you try main now? Should work the way you want it to 🙂 But you'll add read_action : read_alarm I've added it to patch and delete Will likely need to add it to others as well, but this gets the initial support going
Jan Ulbrich
Jan UlbrichOP3y ago
Wow, will do! The read_action does the trick now! 👏 So I can ship that tomorrow. Will go to sleep now happily… 😄 🙃 😴 Thank you very much! 🤩
ZachDaniel
ZachDaniel3y ago
🥳 glad I could help!

Did you find this page helpful?