Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow interface fields to reference other interfaces (#1089) #1091

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -191,6 +191,18 @@ defmodule Absinthe.Phase.Schema.Validation.ObjectMustImplementInterfaces do
:ok
end

defp check_covariant(
%Blueprint.TypeReference.Name{name: iface_name},
%Blueprint.TypeReference.Name{name: type_name},
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ahh, I suspect the difference between the SDL & DSL is that only SDL currently generates the TypeReference blueprint structs everywhere... DSL winds up with bare-atom identifiers

This is a related note from a while back: #783

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You're exactly right! Thats what I was seeing, just a bare atom in the case of the DSL.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ooh nice find

field_ident,
types
) do
{_, itype} = Enum.find(types, fn {_, %{name: name}} -> name == iface_name end)
{_, type} = Enum.find(types, fn {_, %{name: name}} -> name == type_name end)

check_covariant(itype, type, field_ident, types)
end

defp check_covariant(nil, _, field_ident, _), do: {:error, field_ident}
defp check_covariant(_, nil, field_ident, _), do: {:error, field_ident}

Expand Down
28 changes: 28 additions & 0 deletions test/absinthe/schema/notation/experimental/import_sdl_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ defmodule Absinthe.Schema.Notation.Experimental.ImportSdlTest do
scalarEcho(input: CoolScalar): CoolScalar
namedThings: [Named]
titledThings: [Titled]
playerField: PlayerInterface
}

scalar CoolScalar
Expand Down Expand Up @@ -97,6 +98,22 @@ defmodule Absinthe.Schema.Notation.Experimental.ImportSdlTest do
duration: Int!
}

interface PlayerInterface {
metadata: PlayerMetadataInterface
}

interface PlayerMetadataInterface {
displayName: String
}

type HumanPlayer implements PlayerInterface {
metadata: HumanMetadata
}

type HumanMetadata implements PlayerMetadataInterface {
displayName: String
}

scalar B

union SearchResult = Post | User
Expand Down Expand Up @@ -173,6 +190,14 @@ defmodule Absinthe.Schema.Notation.Experimental.ImportSdlTest do
[{:resolve_type, &__MODULE__.titled_resolve_type/2}]
end

def hydrate(%{identifier: :player_interface}, _) do
[{:resolve_type, &__MODULE__.player_interface/2}]
end

def hydrate(%{identifier: :player_metadata_interface}, _) do
[{:resolve_type, &__MODULE__.player_metadata_interface/2}]
end

def hydrate(%{identifier: :content}, _) do
[{:resolve_type, &__MODULE__.content_resolve_type/2}]
end
Expand Down Expand Up @@ -231,6 +256,9 @@ defmodule Absinthe.Schema.Notation.Experimental.ImportSdlTest do

def parse_cool_scalar(value), do: {:ok, value}
def serialize_cool_scalar(%{value: value}), do: value

def player_interface(_, _), do: :human_player
def player_metadata_interface(_, _), do: :human_metadata
end

describe "custom prototype schema" do
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,60 @@ defmodule Absinthe.Schema.Rule.ObjectMustImplementInterfacesTest do
InterfaceImplementsInterfaces.__absinthe_interface_implementors__()
end

defmodule InterfaceFieldsReferenceInterfaces do
use Absinthe.Schema

import_sdl """
interface Pet {
food: PetFood!
}

interface PetFood {
brand: String!
}

type Dog implements Pet {
food: DogFood!
}

type DogFood implements PetFood {
brand: String!
}

type Cat implements Pet {
food: CatFood!
}

type CatFood implements PetFood {
brand: String!
}
"""

query do
end

def hydrate(%{identifier: :pet}, _) do
[{:resolve_type, &__MODULE__.pet/2}]
end

def hydrate(%{identifier: :pet_food}, _) do
[{:resolve_type, &__MODULE__.pet_food/2}]
end

def hydrate(_, _), do: []

def pet(_, _), do: nil
def pet_food(_, _), do: nil
end

test "interface fields can reference other interfaces" do
assert %{
pet: [:cat, :dog],
pet_food: [:cat_food, :dog_food]
} ==
InterfaceFieldsReferenceInterfaces.__absinthe_interface_implementors__()
end

test "is enforced" do
assert_schema_error("invalid_interface_types", [
%{
Expand Down