mirror of
https://github.com/ash-project/ash_graphql.git
synced 2024-09-20 05:13:33 +12:00
66d2f44443
Adds a new option for queries and mutations that defines which arguments or attributes will use a global Relay ID and their type. This allows automatically decoding them before hitting their action. This paves the way to automatic translation derived from the arguments, which will be implemented subsequently. --------- Co-authored-by: Zach Daniel <zachary.s.daniel@gmail.com>
534 lines
12 KiB
Elixir
534 lines
12 KiB
Elixir
defmodule AshGraphql.RelayIdsTest do
|
|
use ExUnit.Case, async: false
|
|
|
|
alias AshGraphql.Test.RelayIds.{Api, Post, ResourceWithNoPrimaryKeyGet, Schema, User}
|
|
|
|
setup do
|
|
on_exit(fn ->
|
|
AshGraphql.TestHelpers.stop_ets()
|
|
end)
|
|
end
|
|
|
|
describe "relay global ID" do
|
|
test "can be used in get queries and is exposed correctly in relationships" do
|
|
user =
|
|
User
|
|
|> Ash.Changeset.for_create(:create, %{name: "fred"})
|
|
|> Api.create!()
|
|
|
|
post =
|
|
Post
|
|
|> Ash.Changeset.for_create(
|
|
:create,
|
|
%{
|
|
author_id: user.id,
|
|
text: "foo",
|
|
published: true
|
|
}
|
|
)
|
|
|> Api.create!()
|
|
|
|
user_relay_id = AshGraphql.Resource.encode_relay_id(user)
|
|
post_relay_id = AshGraphql.Resource.encode_relay_id(post)
|
|
|
|
resp =
|
|
"""
|
|
query GetPost($id: ID!) {
|
|
getPost(id: $id) {
|
|
text
|
|
author {
|
|
id
|
|
name
|
|
}
|
|
}
|
|
}
|
|
"""
|
|
|> Absinthe.run(Schema,
|
|
variables: %{
|
|
"id" => post_relay_id
|
|
}
|
|
)
|
|
|
|
assert {:ok, result} = resp
|
|
|
|
refute Map.has_key?(result, :errors)
|
|
|
|
assert %{
|
|
data: %{
|
|
"getPost" => %{
|
|
"text" => "foo",
|
|
"author" => %{"id" => ^user_relay_id, "name" => "fred"}
|
|
}
|
|
}
|
|
} = result
|
|
end
|
|
|
|
test "returns error on invalid ID" do
|
|
resp =
|
|
"""
|
|
query GetPost($id: ID!) {
|
|
getPost(id: $id) {
|
|
text
|
|
}
|
|
}
|
|
"""
|
|
|> Absinthe.run(Schema,
|
|
variables: %{
|
|
"id" => "invalid"
|
|
}
|
|
)
|
|
|
|
assert {:ok, result} = resp
|
|
assert [%{code: "invalid_primary_key"}] = result[:errors]
|
|
end
|
|
|
|
test "returns error on ID for wrong resource" do
|
|
user =
|
|
User
|
|
|> Ash.Changeset.for_create(:create, %{name: "fred"})
|
|
|> Api.create!()
|
|
|
|
user_relay_id = AshGraphql.Resource.encode_relay_id(user)
|
|
|
|
resp =
|
|
"""
|
|
query GetPost($id: ID!) {
|
|
getPost(id: $id) {
|
|
text
|
|
}
|
|
}
|
|
"""
|
|
|> Absinthe.run(Schema,
|
|
variables: %{
|
|
"id" => user_relay_id
|
|
}
|
|
)
|
|
|
|
assert {:ok, result} = resp
|
|
assert [%{code: "invalid_primary_key"}] = result[:errors]
|
|
end
|
|
end
|
|
|
|
describe "node interface and query" do
|
|
test "allows retrieving resources" do
|
|
user =
|
|
User
|
|
|> Ash.Changeset.for_create(:create, %{name: "fred"})
|
|
|> Api.create!()
|
|
|
|
post =
|
|
Post
|
|
|> Ash.Changeset.for_create(
|
|
:create,
|
|
%{
|
|
author_id: user.id,
|
|
text: "foo",
|
|
published: true
|
|
}
|
|
)
|
|
|> Api.create!()
|
|
|
|
user_relay_id = AshGraphql.Resource.encode_relay_id(user)
|
|
post_relay_id = AshGraphql.Resource.encode_relay_id(post)
|
|
|
|
document =
|
|
"""
|
|
query Node($id: ID!) {
|
|
node(id: $id) {
|
|
__typename
|
|
|
|
... on User {
|
|
name
|
|
}
|
|
|
|
... on Post {
|
|
text
|
|
}
|
|
}
|
|
}
|
|
"""
|
|
|
|
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"
|
|
}
|
|
}
|
|
} = result
|
|
|
|
resp =
|
|
document
|
|
|> Absinthe.run(Schema,
|
|
variables: %{
|
|
"id" => user_relay_id
|
|
}
|
|
)
|
|
|
|
assert {:ok, result} = resp
|
|
|
|
refute Map.has_key?(result, :errors)
|
|
|
|
assert %{
|
|
data: %{
|
|
"node" => %{
|
|
"__typename" => "User",
|
|
"name" => "fred"
|
|
}
|
|
}
|
|
} = result
|
|
end
|
|
|
|
test "return an error for resources without a primary key get" do
|
|
resource =
|
|
ResourceWithNoPrimaryKeyGet
|
|
|> Ash.Changeset.for_create(:create, %{name: "foo"})
|
|
|> Api.create!()
|
|
|
|
document =
|
|
"""
|
|
query Node($id: ID!) {
|
|
node(id: $id) {
|
|
__typename
|
|
|
|
... on ResourceWithNoPrimaryKeyGet{
|
|
name
|
|
}
|
|
}
|
|
}
|
|
"""
|
|
|
|
resource_relay_id = AshGraphql.Resource.encode_relay_id(resource)
|
|
|
|
resp =
|
|
document
|
|
|> Absinthe.run(Schema,
|
|
variables: %{
|
|
"id" => resource_relay_id
|
|
}
|
|
)
|
|
|
|
assert {:ok, result} = resp
|
|
|
|
assert result[:errors] != nil
|
|
end
|
|
end
|
|
|
|
describe "relay ID decoding" do
|
|
test "round trips" do
|
|
user =
|
|
User
|
|
|> Ash.Changeset.for_create(:create, %{name: "Fred"})
|
|
|> Api.create!()
|
|
|
|
user_id = user.id
|
|
user_type = AshGraphql.Resource.Info.type(User)
|
|
|
|
user_relay_id = AshGraphql.Resource.encode_relay_id(user)
|
|
|
|
assert {:ok, %{type: ^user_type, id: ^user_id}} =
|
|
AshGraphql.Resource.decode_relay_id(user_relay_id)
|
|
end
|
|
|
|
test "fails for invalid ids" do
|
|
assert {:error, %Ash.Error.Invalid.InvalidPrimaryKey{}} =
|
|
AshGraphql.Resource.decode_relay_id("notbase64")
|
|
|
|
assert {:error, %Ash.Error.Invalid.InvalidPrimaryKey{}} =
|
|
"non-existing-type:1234"
|
|
|> Base.encode64()
|
|
|> AshGraphql.Resource.decode_relay_id()
|
|
|
|
assert {:error, %Ash.Error.Invalid.InvalidPrimaryKey{}} =
|
|
"user"
|
|
|> Base.encode64()
|
|
|> AshGraphql.Resource.decode_relay_id()
|
|
end
|
|
end
|
|
|
|
describe "relay ID translation" do
|
|
test "works with create mutations" do
|
|
author_id =
|
|
User
|
|
|> Ash.Changeset.for_create(:create, %{name: "Fred"})
|
|
|> Api.create!()
|
|
|> AshGraphql.Resource.encode_relay_id()
|
|
|
|
resp =
|
|
"""
|
|
mutation SimpleCreatePost($input: SimpleCreatePostInput) {
|
|
simpleCreatePost(input: $input) {
|
|
result {
|
|
text
|
|
author {
|
|
id
|
|
}
|
|
}
|
|
errors {
|
|
message
|
|
}
|
|
}
|
|
}
|
|
"""
|
|
|> Absinthe.run(Schema,
|
|
variables: %{
|
|
"input" => %{
|
|
"text" => "foo",
|
|
"author_id" => author_id
|
|
}
|
|
}
|
|
)
|
|
|
|
assert {:ok, result} = resp
|
|
|
|
refute Map.has_key?(result, :errors)
|
|
|
|
assert %{
|
|
data: %{
|
|
"simpleCreatePost" => %{
|
|
"result" => %{
|
|
"text" => "foo",
|
|
"author" => %{
|
|
"id" => ^author_id
|
|
}
|
|
}
|
|
}
|
|
}
|
|
} = result
|
|
end
|
|
|
|
test "works in update mutations" do
|
|
author_id =
|
|
User
|
|
|> Ash.Changeset.for_create(:create, %{name: "Fred"})
|
|
|> Api.create!()
|
|
|> AshGraphql.Resource.encode_relay_id()
|
|
|
|
post_id =
|
|
Post
|
|
|> Ash.Changeset.for_create(:create, %{text: "foo"})
|
|
|> Api.create!()
|
|
|> AshGraphql.Resource.encode_relay_id()
|
|
|
|
resp =
|
|
"""
|
|
mutation AssignAuthor($id: ID!, $input: AssignAuthorInput) {
|
|
assignAuthor(id: $id, input: $input) {
|
|
result {
|
|
text
|
|
author {
|
|
id
|
|
}
|
|
}
|
|
errors {
|
|
message
|
|
}
|
|
}
|
|
}
|
|
"""
|
|
|> Absinthe.run(Schema,
|
|
variables: %{
|
|
"id" => post_id,
|
|
"input" => %{
|
|
"author_id" => author_id
|
|
}
|
|
}
|
|
)
|
|
|
|
assert {:ok, result} = resp
|
|
|
|
refute Map.has_key?(result, :errors)
|
|
|
|
assert %{
|
|
data: %{
|
|
"assignAuthor" => %{
|
|
"result" => %{
|
|
"author" => %{
|
|
"id" => ^author_id
|
|
}
|
|
}
|
|
}
|
|
}
|
|
} = result
|
|
end
|
|
|
|
test "works with lists" do
|
|
author_id =
|
|
User
|
|
|> Ash.Changeset.for_create(:create, %{name: "Fred"})
|
|
|> Api.create!()
|
|
|> AshGraphql.Resource.encode_relay_id()
|
|
|
|
post_ids =
|
|
Enum.map(1..5, fn i ->
|
|
Post
|
|
|> Ash.Changeset.for_create(:create, %{text: "foo #{i}"})
|
|
|> Api.create!()
|
|
|> AshGraphql.Resource.encode_relay_id()
|
|
end)
|
|
|
|
resp =
|
|
"""
|
|
mutation AssignPosts($id: ID!, $input: AssignPostsInput) {
|
|
assignPosts(id: $id, input: $input) {
|
|
result {
|
|
posts {
|
|
id
|
|
}
|
|
}
|
|
errors {
|
|
message
|
|
}
|
|
}
|
|
}
|
|
"""
|
|
|> Absinthe.run(Schema,
|
|
variables: %{
|
|
"id" => author_id,
|
|
"input" => %{
|
|
"post_ids" => post_ids
|
|
}
|
|
}
|
|
)
|
|
|
|
assert {:ok, result} = resp
|
|
|
|
refute Map.has_key?(result, :errors)
|
|
|
|
assert %{
|
|
data: %{
|
|
"assignPosts" => %{
|
|
"result" => %{
|
|
"posts" => posts
|
|
}
|
|
}
|
|
}
|
|
} = result
|
|
|
|
assert length(posts) == 5
|
|
Enum.each(posts, fn post -> assert post["id"] in post_ids end)
|
|
end
|
|
|
|
test "rejects invalid IDs" do
|
|
author_id =
|
|
User
|
|
|> Ash.Changeset.for_create(:create, %{name: "Fred"})
|
|
|> Api.create!()
|
|
|> AshGraphql.Resource.encode_relay_id()
|
|
|
|
post_ids =
|
|
Enum.map(1..5, fn i ->
|
|
Post
|
|
|> Ash.Changeset.for_create(:create, %{text: "foo #{i}"})
|
|
|> Api.create!()
|
|
|> AshGraphql.Resource.encode_relay_id()
|
|
end)
|
|
|
|
post_ids = ["invalid_id" | post_ids]
|
|
|
|
resp =
|
|
"""
|
|
mutation AssignPosts($id: ID!, $input: AssignPostsInput) {
|
|
assignPosts(id: $id, input: $input) {
|
|
result {
|
|
posts {
|
|
id
|
|
}
|
|
}
|
|
errors {
|
|
fields
|
|
message
|
|
}
|
|
}
|
|
}
|
|
"""
|
|
|> Absinthe.run(Schema,
|
|
variables: %{
|
|
"id" => author_id,
|
|
"input" => %{
|
|
"post_ids" => post_ids
|
|
}
|
|
}
|
|
)
|
|
|
|
assert {:ok, result} = resp
|
|
|
|
assert %{
|
|
data: %{
|
|
"assignPosts" => %{
|
|
"result" => nil,
|
|
"errors" => [
|
|
%{
|
|
"fields" => ["post_ids"],
|
|
"message" => "is invalid"
|
|
}
|
|
]
|
|
}
|
|
}
|
|
} = result
|
|
end
|
|
|
|
test "rejects IDs for another type" do
|
|
author_id =
|
|
User
|
|
|> Ash.Changeset.for_create(:create, %{name: "Fred"})
|
|
|> Api.create!()
|
|
|> AshGraphql.Resource.encode_relay_id()
|
|
|
|
post_ids = [author_id]
|
|
|
|
resp =
|
|
"""
|
|
mutation AssignPosts($id: ID!, $input: AssignPostsInput) {
|
|
assignPosts(id: $id, input: $input) {
|
|
result {
|
|
posts {
|
|
id
|
|
}
|
|
}
|
|
errors {
|
|
fields
|
|
message
|
|
}
|
|
}
|
|
}
|
|
"""
|
|
|> Absinthe.run(Schema,
|
|
variables: %{
|
|
"id" => author_id,
|
|
"input" => %{
|
|
"post_ids" => post_ids
|
|
}
|
|
}
|
|
)
|
|
|
|
assert {:ok, result} = resp
|
|
|
|
assert %{
|
|
data: %{
|
|
"assignPosts" => %{
|
|
"result" => nil,
|
|
"errors" => [
|
|
%{
|
|
"fields" => ["post_ids"],
|
|
"message" => "is invalid"
|
|
}
|
|
]
|
|
}
|
|
}
|
|
} = result
|
|
end
|
|
end
|
|
end
|