Skip to content

Commit

Permalink
fix: load relationships and calculations in fragments (#246)
Browse files Browse the repository at this point in the history
  • Loading branch information
rbino authored Nov 24, 2024
1 parent dbbe082 commit 898ff13
Show file tree
Hide file tree
Showing 8 changed files with 196 additions and 12 deletions.
3 changes: 2 additions & 1 deletion lib/ash_graphql.ex
Original file line number Diff line number Diff line change
Expand Up @@ -670,7 +670,8 @@ defmodule AshGraphql do
resource,
resolution,
resolution.path,
resolution.context
resolution.context,
nil
)
end

Expand Down
39 changes: 30 additions & 9 deletions lib/graphql/resolver.ex
Original file line number Diff line number Diff line change
Expand Up @@ -207,7 +207,8 @@ defmodule AshGraphql.Graphql.Resolver do
resource,
resolution,
resolution.path,
context
context,
type_name
)
|> Ash.read_one(opts)

Expand All @@ -232,7 +233,8 @@ defmodule AshGraphql.Graphql.Resolver do
resource,
resolution,
resolution.path,
context
context,
type_name
)

{{:error, error}, [query, {:error, error}]}
Expand Down Expand Up @@ -347,7 +349,8 @@ defmodule AshGraphql.Graphql.Resolver do
resource,
resolution,
resolution.path,
context
context,
type_name
)

result =
Expand Down Expand Up @@ -472,6 +475,7 @@ defmodule AshGraphql.Graphql.Resolver do
resolution,
resolution.path,
context,
type_name,
result_fields
),
{:ok, page} <- Ash.read(query, opts) do
Expand Down Expand Up @@ -1428,6 +1432,7 @@ defmodule AshGraphql.Graphql.Resolver do
resolution,
resolution.path,
context,
type_name,
["result"]
)

Expand Down Expand Up @@ -1562,6 +1567,7 @@ defmodule AshGraphql.Graphql.Resolver do
resolution,
resolution.path,
context,
mutation_result_type(mutation_name),
["result"]
)
)
Expand Down Expand Up @@ -1727,6 +1733,7 @@ defmodule AshGraphql.Graphql.Resolver do
resolution,
resolution.path,
context,
mutation_result_type(mutation_name),
["result"]
)
)
Expand Down Expand Up @@ -1934,9 +1941,11 @@ defmodule AshGraphql.Graphql.Resolver do
resolution,
path,
context,
type_override,
nested \\ []
) do
load = get_loads(load_opts, resource, resolution, path, context, nested)
load =
get_loads(load_opts, resource, resolution, path, context, type_override, nested)

case query_or_changeset do
%Ash.Query{} = query ->
Expand All @@ -1954,26 +1963,37 @@ defmodule AshGraphql.Graphql.Resolver do
resolution,
path,
context,
type_override,
nested \\ []
) do
{fields, path} = nested_fields_and_path(resolution, path, nested)
type_override = type_override || AshGraphql.Resource.Info.type(resource)

{fields, path} = nested_fields_and_path(resolution, path, nested, type_override)

resource_loads(fields, resource, resolution, load_opts, path, context)
end

defp nested_fields_and_path(resolution, path, []) do
defp nested_fields_and_path(resolution, path, nested, type \\ nil)

defp nested_fields_and_path(resolution, path, [], type) do
base = Enum.at(path, 0) || resolution

selections =
case base do
%Absinthe.Resolution{} ->
Absinthe.Resolution.project(resolution)
if type do
Absinthe.Resolution.project(resolution, type)
else
Absinthe.Resolution.project(resolution)
end

%Absinthe.Blueprint.Document.Field{selections: selections} ->
projection_type = type || base.schema_node.type

{fields, _} =
selections
|> Absinthe.Resolution.Projector.project(
Absinthe.Schema.lookup_type(resolution.schema, base.schema_node.type),
Absinthe.Schema.lookup_type(resolution.schema, projection_type),
path,
%{},
resolution
Expand All @@ -1985,7 +2005,7 @@ defmodule AshGraphql.Graphql.Resolver do
{selections, path}
end

defp nested_fields_and_path(resolution, path, [nested | rest]) do
defp nested_fields_and_path(resolution, path, [nested | rest], _type) do
base = Enum.at(path, 0) || resolution

selections =
Expand Down Expand Up @@ -2193,6 +2213,7 @@ defmodule AshGraphql.Graphql.Resolver do
selection | path
],
context,
nil,
result_fields
)

Expand Down
3 changes: 2 additions & 1 deletion lib/subscriptions.ex
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,8 @@ defmodule AshGraphql.Subscription do
query.resource,
resolution,
resolution.path,
context
context,
type_override
)
end
end
91 changes: 90 additions & 1 deletion test/relay_ids_test.exs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
defmodule AshGraphql.RelayIdsTest do
use ExUnit.Case, async: false

alias AshGraphql.Test.RelayIds.{Post, ResourceWithNoPrimaryKeyGet, Schema, User}
alias AshGraphql.Test.RelayIds.{Comment, Post, ResourceWithNoPrimaryKeyGet, Schema, User}

setup do
on_exit(fn ->
Expand Down Expand Up @@ -189,6 +189,95 @@ defmodule AshGraphql.RelayIdsTest do
} = result
end

test "allow loading nested relationships and calculations" do
user =
User
|> Ash.Changeset.for_create(:create, %{name: "fred"})
|> Ash.create!()

commenter =
User
|> Ash.Changeset.for_create(:create, %{name: "george"})
|> Ash.create!()

post =
Post
|> Ash.Changeset.for_create(
:create,
%{
author_id: user.id,
text: "foo"
}
)
|> Ash.create!()

Comment
|> Ash.Changeset.for_create(:create, %{
text: "nice",
author_id: commenter.id,
post_id: post.id
})
|> Ash.create!()

post_relay_id = AshGraphql.Resource.encode_relay_id(post)

document =
"""
query Node($id: ID!) {
node(id: $id) {
__typename
... on Post {
text
author {
name
}
comments {
text
author {
name
nameTwice
}
}
}
}
}
"""

resp =
document
|> Absinthe.run(Schema,
variables: %{
"id" => post_relay_id
}
)

assert {:ok, result} = resp

refute Map.has_key?(result, :errors)

assert %{
data: %{
"node" => %{
"__typename" => "Post",
"text" => "foo",
"author" => %{
"name" => "fred"
},
"comments" => [
%{
"text" => "nice",
"author" => %{
"name" => "george",
"nameTwice" => "george george"
}
}
]
}
}
} = result
end

test "return an error for resources without a primary key get" do
resource =
ResourceWithNoPrimaryKeyGet
Expand Down
1 change: 1 addition & 0 deletions test/support/relay_ids/domain.ex
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ defmodule AshGraphql.Test.RelayIds.Domain do
otp_app: :ash_graphql

resources do
resource(AshGraphql.Test.RelayIds.Comment)
resource(AshGraphql.Test.RelayIds.Post)
resource(AshGraphql.Test.RelayIds.ResourceWithNoPrimaryKeyGet)
resource(AshGraphql.Test.RelayIds.User)
Expand Down
65 changes: 65 additions & 0 deletions test/support/relay_ids/resources/comment.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
defmodule AshGraphql.Test.RelayIds.Comment do
@moduledoc false

use Ash.Resource,
domain: AshGraphql.Test.RelayIds.Domain,
data_layer: Ash.DataLayer.Ets,
extensions: [AshGraphql.Resource]

graphql do
type :comment

queries do
list :list_comments, :read
end

mutations do
create :create_comment, :create
end
end

actions do
default_accept(:*)
defaults([:create, :update, :destroy])

read :read do
primary?(true)
end

read :paginated do
pagination(required?: true, offset?: true, countable: true)
end
end

attributes do
uuid_primary_key(:id)
attribute(:text, :string, public?: true)

attribute :type, :atom do
public?(true)
writable?(false)
default(:comment)
constraints(one_of: [:comment, :reply])
end

create_timestamp(:created_at)
end

calculations do
calculate(
:timestamp,
:utc_datetime_usec,
expr(created_at),
public?: true
)
end

relationships do
belongs_to(:post, AshGraphql.Test.RelayIds.Post, public?: true)

belongs_to :author, AshGraphql.Test.RelayIds.User do
public?(true)
attribute_writable?(true)
end
end
end
2 changes: 2 additions & 0 deletions test/support/relay_ids/resources/post.ex
Original file line number Diff line number Diff line change
Expand Up @@ -52,5 +52,7 @@ defmodule AshGraphql.Test.RelayIds.Post do
public?(true)
attribute_writable?(true)
end

has_many(:comments, AshGraphql.Test.RelayIds.Comment, public?: true)
end
end
4 changes: 4 additions & 0 deletions test/support/relay_ids/resources/user.ex
Original file line number Diff line number Diff line change
Expand Up @@ -42,4 +42,8 @@ defmodule AshGraphql.Test.RelayIds.User do
public?: true
)
end

calculations do
calculate(:name_twice, :string, expr(name <> " " <> name), public?: true)
end
end

0 comments on commit 898ff13

Please sign in to comment.