AshJsonApi + Managed Relationships

Are there any examples of AshJsonApi, the docs seem outdated. Im trying to get managed relationships to work through a 'parent' resource.
14 Replies
gordoneliel
gordonelielOP•2y ago
This is my current setup for a "Location" resource and an "Area" managed relationship:
...Location resouce which has many areas

relationships do
has_many :areas, Area
end

json_api do
type "location"

includes(areas: [])

routes do
base("/locations")

get(:read)
index :read
post(:create)
delete(:destroy)

related(:areas, :read)

relationship(:areas, :read)

post_to_relationship(:areas)
patch_relationship(:areas)
delete_from_relationship(:areas)
end
end

update :update do
argument :areas, :map do
allow_nil? false
end

change manage_relationship(:areas, type: :create)
end
...Location resouce which has many areas

relationships do
has_many :areas, Area
end

json_api do
type "location"

includes(areas: [])

routes do
base("/locations")

get(:read)
index :read
post(:create)
delete(:destroy)

related(:areas, :read)

relationship(:areas, :read)

post_to_relationship(:areas)
patch_relationship(:areas)
delete_from_relationship(:areas)
end
end

update :update do
argument :areas, :map do
allow_nil? false
end

change manage_relationship(:areas, type: :create)
end
When trying to get http://localhost:4000/api/locations/55b9cd3c-8a83-431e-b580-98365bc2b5ab/relationships/areas, I get a 500
gordoneliel
gordonelielOP•2y ago
gordoneliel
gordonelielOP•2y ago
This is the Area resource
...Area resource


json_api do
type "area"

routes do
base("/areas")

get(:read)
index :read
post(:create, relationship_arguments: [{:id, :location}])
patch(:update, relationship_arguments: [:location])
delete(:destroy)

related :location, :read do
primary? true
route("/:id/location")
end

relationship :location, :read do
primary? true
end
end
end

relationships do
belongs_to :location, Location, allow_nil?: false
end
...Area resource


json_api do
type "area"

routes do
base("/areas")

get(:read)
index :read
post(:create, relationship_arguments: [{:id, :location}])
patch(:update, relationship_arguments: [:location])
delete(:destroy)

related :location, :read do
primary? true
route("/:id/location")
end

relationship :location, :read do
primary? true
end
end
end

relationships do
belongs_to :location, Location, allow_nil?: false
end
ZachDaniel
ZachDaniel•2y ago
Oh, okay that looks like a bug that may actually have been fixed already are you on the latest version of ash_json_api? If so, can you try pointing at main? github: "ash-project/ash_json_api", ref: "main"?
gordoneliel
gordonelielOP•2y ago
@Zach Daniel That works! Although it does not return any of its fields, just an id and type
ZachDaniel
ZachDaniel•2y ago
🤔 oh, yeah that is correct relationship/2 creates an endpoint designed to do that if you want an endpoint that returns the full related entities thats when you use related/2 which is typically the difference between /thing/:id/relationships/related and /thing/:id/related https://jsonapi.org/format/#fetching-relationships
gordoneliel
gordonelielOP•2y ago
hm, tried http://localhost:4000/api/locations/55b9cd3c-8a83-431e-b580-98365bc2b5ab/areas but got a 404? sorry, actually worked!
gordoneliel
gordonelielOP•2y ago
Not getting related links for included relationships though, is that right?
No description
ZachDaniel
ZachDaniel•2y ago
looks like a get_related route needs to be marked as primary to show up there
defp add_related_link(links, request, %resource{} = record, relationship) do
resource
|> AshJsonApi.Resource.route(%{
relationship: relationship.name,
primary?: true,
action_type: :get_related
})
|> case do
nil ->
links

%{route: route} ->
link =
request
|> with_path_params(%{"id" => AshJsonApi.Resource.encode_primary_key(record)})
|> at_host(route)

Map.put(links, "related", link)
end
end
defp add_related_link(links, request, %resource{} = record, relationship) do
resource
|> AshJsonApi.Resource.route(%{
relationship: relationship.name,
primary?: true,
action_type: :get_related
})
|> case do
nil ->
links

%{route: route} ->
link =
request
|> with_path_params(%{"id" => AshJsonApi.Resource.encode_primary_key(record)})
|> at_host(route)

Map.put(links, "related", link)
end
end
So if you set the get_related to primary? true then it should appear in the links its been a long time since I made that choice, but I imagine it was to solve for cases where there were multiple endpoints to get the same relationship That could probably be improved 🙂
gordoneliel
gordonelielOP•2y ago
That worked too! Im trying to patch a managed resource through the main one, eg: http://localhost:4000/api/locations/55b9cd3c-8a83-431e-b580-98365bc2b5ab Curl eg:
PATCH /api/locations/55b9cd3c-8a83-431e-b580-98365bc2b5ab HTTP/1.1
x-api-key: Yc8ccgMNfDcTCDGKWdotZ+1aemcJswQq
x-tenant-id: ef19b3ea-99d8-4fe4-9622-af6931379c6d
Content-Type: application/vnd.api+json
Host: localhost:4000
Connection: close
User-Agent: RapidAPI/4.2.0 (Macintosh; OS X/13.4.1) GCDHTTPRequest
Content-Length: 148

{"data":{"type":"location","relationships":{"areas":{"data":{"type":"area","name":"Train Station X"}}},"id":"55b9cd3c-8a83-431e-b580-98365bc2b5ab"}}
PATCH /api/locations/55b9cd3c-8a83-431e-b580-98365bc2b5ab HTTP/1.1
x-api-key: Yc8ccgMNfDcTCDGKWdotZ+1aemcJswQq
x-tenant-id: ef19b3ea-99d8-4fe4-9622-af6931379c6d
Content-Type: application/vnd.api+json
Host: localhost:4000
Connection: close
User-Agent: RapidAPI/4.2.0 (Macintosh; OS X/13.4.1) GCDHTTPRequest
Content-Length: 148

{"data":{"type":"location","relationships":{"areas":{"data":{"type":"area","name":"Train Station X"}}},"id":"55b9cd3c-8a83-431e-b580-98365bc2b5ab"}}
Any idea how that works? I have this on the main resource
get(:read)
index :read
post(:create)
patch(:update)
delete(:destroy)

related :areas, :read do
primary? true
end

relationship(:areas, :read)

post_to_relationship(:areas)
patch_relationship(:areas)
delete_from_relationship(:areas)
get(:read)
index :read
post(:create)
patch(:update)
delete(:destroy)

related :areas, :read do
primary? true
end

relationship(:areas, :read)

post_to_relationship(:areas)
patch_relationship(:areas)
delete_from_relationship(:areas)
ZachDaniel
ZachDaniel•2y ago
You provide it as an attribute in that case the map of data to be passed into the managed relationship I mean we pull arguments and attributes out of the attributes field (because JSON:API doesn't give us anywhere else to accept input AFAIK) released 0.32.1 so you don't need to be on a GH branch 🙂
gordoneliel
gordonelielOP•2y ago
Looking at the spec for jsonapi here: https://jsonapi.org/format/#crud-updating-resource-relationships Looks like you can update from passing it as a relationship on patch?
ZachDaniel
ZachDaniel•2y ago
actually yeah it looks like we will search for arguments meant to manage relationships ah, thats right, its configured
routes do
patch :update, relationship_arguments: [:authors]
end
routes do
patch :update, relationship_arguments: [:authors]
end
You say which arguments are edited in the relationships

Did you find this page helpful?