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

Add "Objects must define fields" schema validation #1167

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
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
- Bug Fix: [Add `__private__` field to EnumValueDefinition](https://github.com/absinthe-graphql/absinthe/pull/1148)
- Bug Fix: [Fix bug in Schema.**absinthe_types**(:all) for Persistent Term](https://github.com/absinthe-graphql/absinthe/pull/1161)
- Bug Fix: [Fix default enum value check for SDL schema's](https://github.com/absinthe-graphql/absinthe/pull/1188)
- Bug Fix: [Add "Objects must define fields" schema validation](https://github.com/absinthe-graphql/absinthe/pull/1167)
- Feature: [Add `import_directives` macro](https://github.com/absinthe-graphql/absinthe/pull/1158)
- Feature: [Support type extensions on schema declarations](https://github.com/absinthe-graphql/absinthe/pull/1176)
- Bug Fix: [Root objects are marked as referenced correctly](https://github.com/absinthe-graphql/absinthe/pull/1186)
Expand Down
2 changes: 2 additions & 0 deletions lib/absinthe/blueprint/schema.ex
Original file line number Diff line number Diff line change
Expand Up @@ -69,12 +69,14 @@ defmodule Absinthe.Blueprint.Schema do
build_types(attrs, [bp], [])
end

def struct_to_kind(%struct{}), do: struct_to_kind(struct)
def struct_to_kind(Blueprint.Schema.DirectiveDefinition), do: "directive"
def struct_to_kind(Blueprint.Schema.EnumTypeDefinition), do: "enum type"
def struct_to_kind(Blueprint.Schema.EnumValueDefinition), do: "enum value"
def struct_to_kind(Blueprint.Schema.FieldDefinition), do: "field"
def struct_to_kind(Blueprint.Schema.InputObjectTypeDefinition), do: "input object"
def struct_to_kind(Blueprint.Schema.InputValueDefinition), do: "argument"
def struct_to_kind(Blueprint.Schema.InterfaceTypeDefinition), do: "interface"
def struct_to_kind(Blueprint.Schema.ObjectTypeDefinition), do: "object"
def struct_to_kind(Blueprint.Schema.ScalarTypeDefinition), do: "scalar"
def struct_to_kind(Blueprint.Schema.UnionTypeDefinition), do: "union"
Expand Down
61 changes: 61 additions & 0 deletions lib/absinthe/phase/schema/validation/object_must_define_fields.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
defmodule Absinthe.Phase.Schema.Validation.ObjectMustDefineFields do
@moduledoc false

use Absinthe.Phase
alias Absinthe.Blueprint

def run(bp, _) do
bp = Blueprint.prewalk(bp, &handle_schemas/1)
{:ok, bp}
end

defp handle_schemas(%Blueprint.Schema.SchemaDefinition{} = schema) do
schema = Blueprint.prewalk(schema, &validate_objects/1)
{:halt, schema}
end

defp handle_schemas(obj) do
obj
end

defp validate_objects(%struct{} = node)
when struct in [
Blueprint.Schema.ObjectTypeDefinition,
Blueprint.Schema.InterfaceTypeDefinition,
Blueprint.Schema.InputObjectTypeDefinition
] do
if defines_fields?(node) do
node
else
put_error(node, error(node))
end
end

defp validate_objects(node) do
node
end

defp defines_fields?(%{fields: []}) do
false
end

defp defines_fields?(object) do
!Enum.all?(object.fields, &introspection?(&1))
end

defp introspection?(%{name: "__" <> _}), do: true
defp introspection?(_), do: false

defp error(object) do
%Absinthe.Phase.Error{
message: explanation(object),
locations: [object.__reference__.location],
phase: __MODULE__
}
end

def explanation(object) do
kind = Absinthe.Blueprint.Schema.struct_to_kind(object)
"The #{kind} type `#{object.identifier}` must define one or more fields."
end
end
1 change: 1 addition & 0 deletions lib/absinthe/pipeline.ex
Original file line number Diff line number Diff line change
Expand Up @@ -176,6 +176,7 @@ defmodule Absinthe.Pipeline do
Phase.Schema.Directives,
Phase.Schema.Validation.DefaultEnumValuePresent,
Phase.Schema.Validation.DirectivesMustBeValid,
Phase.Schema.Validation.ObjectMustDefineFields,
Phase.Schema.Validation.InputOutputTypesCorrectlyPlaced,
Phase.Schema.Validation.InterfacesMustResolveTypes,
Phase.Schema.Validation.ObjectInterfacesMustBeValid,
Expand Down
2 changes: 1 addition & 1 deletion test/absinthe/execution/subscription_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ defmodule Absinthe.Execution.SubscriptionTest do
use Absinthe.Schema

query do
# Query type must exist
field :foo, :string
end

object :user do
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,11 @@ defmodule Elixir.Absinthe.Integration.Execution.Introspection.FullTest do
use Absinthe.Schema

query do
field :foo, :string
end

def middleware(middleware, %{identifier: :foo}, _) do
middleware
end

def middleware(_, _, _) do
Expand Down
2 changes: 1 addition & 1 deletion test/absinthe/phase/schema/reformat_description_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ defmodule Absinthe.Phase.Schema.ReformatDescriptionTest do
use Absinthe.Schema

query do
# Must exist
field :foo, :string
end

object :via_macro do
Expand Down
2 changes: 1 addition & 1 deletion test/absinthe/pipeline_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ defmodule Absinthe.PipelineTest do
use Absinthe.Schema

query do
# Query type must exist
field :foo, :string
end
end

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ defmodule Absinthe.Schema.Notation.Experimental.ImportDirectivesTest do
use Absinthe.Schema

query do
field :foo, :string
end

import_directives Source
Expand All @@ -32,6 +33,7 @@ defmodule Absinthe.Schema.Notation.Experimental.ImportDirectivesTest do
use Absinthe.Schema

query do
field :foo, :string
end

import_directives Source, only: [:one, :two]
Expand All @@ -41,12 +43,12 @@ defmodule Absinthe.Schema.Notation.Experimental.ImportDirectivesTest do
use Absinthe.Schema

query do
field :foo, :string
end

import_directives Source, except: [:one, :two]
end

@tag :skip
describe "import_directives" do
test "without options" do
assert [{Source, []}] == imports(WithoutOptions)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ defmodule Absinthe.Schema.Notation.Experimental.ImportTypeExtensionsTest do
use Absinthe.Schema

query do
field :foo, :string
end

object :one do
Expand All @@ -41,6 +42,7 @@ defmodule Absinthe.Schema.Notation.Experimental.ImportTypeExtensionsTest do
use Absinthe.Schema

query do
field :foo, :string
end

object :one do
Expand All @@ -50,6 +52,7 @@ defmodule Absinthe.Schema.Notation.Experimental.ImportTypeExtensionsTest do
end

object :three do
field :foo, :string
end

import_type_extensions Source, only: [:one, :two]
Expand All @@ -59,12 +62,15 @@ defmodule Absinthe.Schema.Notation.Experimental.ImportTypeExtensionsTest do
use Absinthe.Schema

query do
field :foo, :string
end

object :one do
field :foo, :string
end

object :two do
field :foo, :string
end

object :three do
Expand All @@ -87,14 +93,14 @@ defmodule Absinthe.Schema.Notation.Experimental.ImportTypeExtensionsTest do

assert [%{identifier: :one}] = fields(UsingOnlyOption, :one)
assert [%{identifier: :two}] = fields(UsingOnlyOption, :two)
assert [] = fields(UsingOnlyOption, :three)
assert [%{identifier: :foo}] = fields(UsingOnlyOption, :three)
end

test "with :except" do
assert [{Source, except: [:one, :two]}] == imports(UsingExceptOption)

assert [] = fields(UsingExceptOption, :one)
assert [] = fields(UsingExceptOption, :two)
assert [%{identifier: :foo}] = fields(UsingExceptOption, :one)
assert [%{identifier: :foo}] = fields(UsingExceptOption, :two)
assert [%{identifier: :three}] = fields(UsingExceptOption, :three)
end
end
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,19 +7,23 @@ defmodule Absinthe.Schema.Notation.Experimental.ImportTypesTest do
use Absinthe.Schema.Notation

object :one do
field :foo, :string
end

object :two do
field :foo, :string
end

object :three do
field :foo, :string
end
end

defmodule WithoutOptions do
use Absinthe.Schema

query do
field :foo, :string
end

import_types Source
Expand All @@ -29,6 +33,7 @@ defmodule Absinthe.Schema.Notation.Experimental.ImportTypesTest do
use Absinthe.Schema

query do
field :foo, :string
end

import_types(Source, only: [:one, :two])
Expand All @@ -38,6 +43,7 @@ defmodule Absinthe.Schema.Notation.Experimental.ImportTypesTest do
use Absinthe.Schema

query do
field :foo, :string
end

import_types(Source, except: [:one, :two])
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ defmodule Absinthe.Schema.Notation.Experimental.MacroExtensionsTest do
@prototype_schema WithFeatureDirective

query do
field :foo, :string
end

extend schema do
Expand Down Expand Up @@ -181,6 +182,10 @@ defmodule Absinthe.Schema.Notation.Experimental.MacroExtensionsTest do
name: "__typename",
type: :string
},
%{
name: "foo",
type: :string
},
%{
name: "value",
type: :integer
Expand Down Expand Up @@ -291,6 +296,7 @@ defmodule Absinthe.Schema.Notation.Experimental.MacroExtensionsTest do
use Absinthe.Schema

query do
field :foo, :string
end

import_type_extensions ImportedSchema
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ defmodule Absinthe.Schema.Notation.Experimental.SdlExtensionsTest do
@prototype_schema WithFeatureDirective

query do
field :foo, :string
end

import_sdl """
Expand Down Expand Up @@ -246,6 +247,7 @@ defmodule Absinthe.Schema.Notation.Experimental.SdlExtensionsTest do
use Absinthe.Schema

query do
field :foo, :string
end

import_type_extensions ImportedSchema
Expand Down
14 changes: 7 additions & 7 deletions test/absinthe/schema/notation/import_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ defmodule Absinthe.Schema.Notation.ImportTest do
use Absinthe.Schema

query do
# Query type must exist
field :foo, :string
end

object :foo do
Expand All @@ -41,7 +41,7 @@ defmodule Absinthe.Schema.Notation.ImportTest do
use Absinthe.Schema

query do
# Query type must exist
field :foo, :string
end

input_object :foo do
Expand All @@ -64,7 +64,7 @@ defmodule Absinthe.Schema.Notation.ImportTest do
use Absinthe.Schema

query do
# Query type must exist
field :foo, :string
end

object :cool_fields do
Expand Down Expand Up @@ -92,7 +92,7 @@ defmodule Absinthe.Schema.Notation.ImportTest do
use Absinthe.Schema

query do
# Query type must exist
field :foo, :string
end

object :foo do
Expand Down Expand Up @@ -211,7 +211,7 @@ defmodule Absinthe.Schema.Notation.ImportTest do
use Absinthe.Schema

query do
# Query type must exist
field :foo, :string
end

object :foo do
Expand All @@ -223,7 +223,7 @@ defmodule Absinthe.Schema.Notation.ImportTest do
use Absinthe.Schema

query do
# Query type must exist
field :foo, :string
end

object :bar do
Expand All @@ -235,7 +235,7 @@ defmodule Absinthe.Schema.Notation.ImportTest do
use Absinthe.Schema

query do
# Query type must exist
field :foo, :string
end

import_types Source1
Expand Down
Loading