Skip to content

Commit

Permalink
Merge pull request #1176 from maartenvanvliet/schema_declaration_dire…
Browse files Browse the repository at this point in the history
…ctives

Allow schema declaration directives
  • Loading branch information
benwilson512 authored Aug 9, 2022
2 parents 6ecb147 + b0047e4 commit a276684
Show file tree
Hide file tree
Showing 22 changed files with 352 additions and 96 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,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)
- 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)

## 1.7.0

Expand Down
23 changes: 22 additions & 1 deletion lib/absinthe/blueprint/schema.ex
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,8 @@ defmodule Absinthe.Blueprint.Schema do
Schema.InterfaceTypeDefinition,
Schema.UnionTypeDefinition,
Schema.EnumValueDefinition,
Schema.TypeExtensionDefinition
Schema.TypeExtensionDefinition,
Schema.SchemaDeclaration
]

defp build_types([%module{} = type | rest], stack, buff) when module in @simple_open do
Expand Down Expand Up @@ -196,6 +197,7 @@ defmodule Absinthe.Blueprint.Schema do
Schema.InterfaceTypeDefinition,
Schema.ObjectTypeDefinition,
Schema.ScalarTypeDefinition,
Schema.SchemaDeclaration,
Schema.UnionTypeDefinition
]
defp build_types(
Expand All @@ -210,6 +212,14 @@ defmodule Absinthe.Blueprint.Schema do
build_types(rest, [%{extend | definition: def} | stack], buff)
end

defp build_types(
[:close | rest],
[%Schema.FieldDefinition{} = field, %Schema.SchemaDeclaration{} = declaration | stack],
buff
) do
build_types(rest, [push(declaration, :field_definitions, field) | stack], buff)
end

defp build_types([:close | rest], [%Schema.FieldDefinition{} = field, obj | stack], buff) do
field =
field
Expand Down Expand Up @@ -271,6 +281,17 @@ defmodule Absinthe.Blueprint.Schema do
build_types(rest, [schema | stack], buff)
end

defp build_types(
[:close | rest],
[%Schema.SchemaDeclaration{} = schema_declaration, schema | stack],
buff
) do
# The declaration is pushed into the :type_definitions instead of the :schema_declaration
# as it will be split off later in the ApplyDeclaration phase
schema = push(schema, :type_definitions, schema_declaration)
build_types(rest, [schema | stack], buff)
end

defp build_types([:close | rest], [%Schema.SchemaDefinition{} = schema, bp], buff) do
bp = push(bp, :schema_definitions, schema)
build_types(rest, [bp], buff)
Expand Down
4 changes: 3 additions & 1 deletion lib/absinthe/blueprint/transform.ex
Original file line number Diff line number Diff line change
Expand Up @@ -86,11 +86,13 @@ defmodule Absinthe.Blueprint.Transform do
Blueprint.Schema.InterfaceTypeDefinition => [:interfaces, :fields, :directives],
Blueprint.Schema.ObjectTypeDefinition => [:interfaces, :fields, :directives],
Blueprint.Schema.ScalarTypeDefinition => [:directives],
Blueprint.Schema.SchemaDeclaration => [:directives, :field_definitions],
Blueprint.Schema.SchemaDefinition => [
:directive_definitions,
:type_definitions,
:type_extensions,
:directives
:directives,
:schema_declaration
],
Blueprint.Schema.TypeExtensionDefinition => [:definition],
Blueprint.Schema.UnionTypeDefinition => [:directives, :types]
Expand Down
34 changes: 33 additions & 1 deletion lib/absinthe/phase/schema/apply_declaration.ex
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,18 @@ defmodule Absinthe.Phase.Schema.ApplyDeclaration do
}

[] ->
schema_definition
declaration = build_declaration(schema_definition.type_definitions)

root_mappings =
declaration
|> extract_root_mappings

%{
schema_definition
| type_definitions:
Enum.map(schema_definition.type_definitions, &maybe_mark_root(&1, root_mappings)),
schema_declaration: declaration
}

[_first | extra_declarations] ->
extra_declarations
Expand Down Expand Up @@ -113,4 +124,25 @@ defmodule Absinthe.Phase.Schema.ApplyDeclaration do
false
end)
end

defp build_declaration(type_definitions) do
field_definitions =
type_definitions
|> Enum.filter(&(&1.identifier in ~w(query mutation subscription)a))
|> Enum.map(fn type_def ->
%Blueprint.Schema.FieldDefinition{
module: __MODULE__,
name: to_string(type_def.identifier),
identifier: type_def.identifier,
type: %Blueprint.TypeReference.Name{name: type_def.name},
__reference__: Absinthe.Schema.Notation.build_reference(__ENV__)
}
end)

%Blueprint.Schema.SchemaDeclaration{
module: __MODULE__,
__reference__: Absinthe.Schema.Notation.build_reference(__ENV__),
field_definitions: field_definitions
}
end
end
27 changes: 25 additions & 2 deletions lib/absinthe/phase/schema/apply_type_extensions.ex
Original file line number Diff line number Diff line change
Expand Up @@ -16,14 +16,22 @@ defmodule Absinthe.Phase.Schema.ApplyTypeExtensions do
end

def update_schema_defs(schema_definitions) do
for schema_def = %{type_definitions: type_definitions, type_extensions: type_extensions} <-
for schema_def = %{
type_definitions: type_definitions,
type_extensions: type_extensions,
schema_declaration: schema_declaration
} <-
schema_definitions do
{type_definitions, type_extensions} =
apply_type_extensions(type_definitions, type_extensions, [])

{[schema_declaration], type_extensions} =
apply_type_extensions([schema_declaration], type_extensions, [])

%{
schema_def
| type_definitions: type_definitions,
| schema_declaration: schema_declaration,
type_definitions: type_definitions,
type_extensions: type_extensions
}
end
Expand Down Expand Up @@ -125,6 +133,21 @@ defmodule Absinthe.Phase.Schema.ApplyTypeExtensions do
}}
end

defp apply_extension(
%Schema.TypeExtensionDefinition{
definition: %Schema.SchemaDeclaration{}
} = extension,
%Schema.SchemaDeclaration{} = definition
) do
{extension,
%{
definition
| field_definitions:
definition.field_definitions ++ extension.definition.field_definitions,
directives: definition.directives ++ extension.definition.directives
}}
end

defp apply_extension(
%{definition: %{identifier: identifier} = extension},
%{identifier: identifier} = definition
Expand Down
4 changes: 4 additions & 0 deletions lib/absinthe/phase/schema/compile.ex
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,10 @@ defmodule Absinthe.Phase.Schema.Compile do
unquote(Macro.escape(prototype_schema))
end

def __absinthe_schema_declaration__() do
unquote(Macro.escape(schema.schema_declaration))
end

unquote_splicing(metadata)
end

Expand Down
3 changes: 2 additions & 1 deletion lib/absinthe/phase/schema/populate_persistent_term.ex
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,8 @@ if Code.ensure_loaded?(:persistent_term) do
__absinthe_prototype_schema__: prototype_schema,
__absinthe_type__: types_map,
__absinthe_directive__: directives_map,
__absinthe_reference__: metadata
__absinthe_reference__: metadata,
__absinthe_schema_declaration__: schema.schema_declaration
}

schema_name = opts[:schema] || raise "no schema name provided"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,13 @@ defmodule Absinthe.Phase.Schema.Validation.TypeReferencesExist do
end

def validate_types(%Blueprint.Schema.TypeExtensionDefinition{} = extension, types) do
check_or_error(extension, extension.definition.identifier, types)
case extension.definition do
%Blueprint.Schema.SchemaDeclaration{} = declaration ->
declaration

definition ->
check_or_error(extension, definition.identifier, types)
end
end

@no_types [
Expand All @@ -61,6 +67,7 @@ defmodule Absinthe.Phase.Schema.Validation.TypeReferencesExist do
Blueprint.Schema.ObjectTypeDefinition,
Blueprint.Schema.ScalarTypeDefinition,
Blueprint.Schema.SchemaDefinition,
Blueprint.Schema.SchemaDeclaration,
Blueprint.TypeReference.NonNull,
Blueprint.TypeReference.ListOf,
Absinthe.Blueprint.TypeReference.Name
Expand Down
2 changes: 1 addition & 1 deletion lib/absinthe/pipeline.ex
Original file line number Diff line number Diff line change
Expand Up @@ -154,8 +154,8 @@ defmodule Absinthe.Pipeline do
{Phase.Schema.DirectiveImports, options},
{Phase.Schema.TypeImports, options},
{Phase.Schema.TypeExtensionImports, options},
Phase.Schema.ApplyTypeExtensions,
Phase.Schema.ApplyDeclaration,
Phase.Schema.ApplyTypeExtensions,
Phase.Schema.Introspection,
{Phase.Schema.Hydrate, options},
Phase.Schema.Arguments.Normalize,
Expand Down
8 changes: 8 additions & 0 deletions lib/absinthe/schema.ex
Original file line number Diff line number Diff line change
Expand Up @@ -329,6 +329,10 @@ defmodule Absinthe.Schema do
@schema_provider.__absinthe_interface_implementors__(__MODULE__)
end

def __absinthe_schema_declaration__() do
@schema_provider.__absinthe_schema_declaration__(__MODULE__)
end

def __absinthe_prototype_schema__() do
@prototype_schema
end
Expand Down Expand Up @@ -547,6 +551,10 @@ defmodule Absinthe.Schema do
end
end

def schema_declaration(schema) do
schema.__absinthe_schema_declaration__()
end

@doc """
Get all concrete types for union, interface, or object
"""
Expand Down
4 changes: 4 additions & 0 deletions lib/absinthe/schema/compiled.ex
Original file line number Diff line number Diff line change
Expand Up @@ -30,4 +30,8 @@ defmodule Absinthe.Schema.Compiled do
def __absinthe_interface_implementors__(schema_mod) do
Module.concat([schema_mod, Compiled]).__absinthe_interface_implementors__
end

def __absinthe_schema_declaration__(schema_mod) do
Module.concat([schema_mod, Compiled]).__absinthe_schema_declaration__
end
end
67 changes: 59 additions & 8 deletions lib/absinthe/schema/notation.ex
Original file line number Diff line number Diff line change
Expand Up @@ -185,15 +185,8 @@ defmodule Absinthe.Schema.Notation do
end
```
"""
@reserved_identifiers ~w(query mutation subscription)a
defmacro object(identifier, attrs \\ [], block)

defmacro object(identifier, _attrs, _block) when identifier in @reserved_identifiers do
raise Absinthe.Schema.Notation.Error,
"Invalid schema notation: cannot create an `object` " <>
"with reserved identifier `#{identifier}`"
end

defmacro object(identifier, attrs, do: block) do
block = block_from_directive_attrs(attrs, block)

Expand Down Expand Up @@ -300,6 +293,41 @@ defmodule Absinthe.Schema.Notation do
|> record_extend!([], block, [])
end

defmacro extend({:schema, meta, _}, do: block) do
block = {:schema, meta, [] ++ [[do: block]]}

__CALLER__
|> recordable!(:extend, @placement[:extend])
|> record_extend!([], block, [])
end

@placement {:schema, [toplevel: true, extend: true]}
@doc """
Declare a schema
Optional declaration of the schema. Useful if you want to add directives
to your schema declaration
## Placement
#{Utils.placement_docs(@placement)}
## Examples
```
schema do
directive :feature
field :query, :query
# ...
end
```
"""
defmacro schema(do: block) do
__CALLER__
|> recordable!(:schema, @placement[:schema])
|> record_schema!(block)
end

@placement {:deprecate, [under: [:field]]}
@doc """
Mark a field as deprecated
Expand Down Expand Up @@ -477,7 +505,7 @@ defmodule Absinthe.Schema.Notation do
end

# FIELDS
@placement {:field, [under: [:input_object, :interface, :object]]}
@placement {:field, [under: [:input_object, :interface, :object, :schema_declaration]]}
@doc """
Defines a GraphQL field
Expand Down Expand Up @@ -932,6 +960,7 @@ defmodule Absinthe.Schema.Notation do
:interface,
:object,
:scalar,
:schema_declaration,
:union,
:value
]
Expand Down Expand Up @@ -1592,6 +1621,25 @@ defmodule Absinthe.Schema.Notation do
]
end

def record_schema!(env, block) do
attrs =
[]
|> Keyword.put(:module, env.module)
|> put_reference(env)

definition = struct!(Schema.SchemaDeclaration, attrs)

ref = put_attr(env.module, definition)

push_stack(env.module, :absinthe_scope_stack, :schema_declaration)

[
get_desc(ref),
block,
quote(do: unquote(__MODULE__).close_scope())
]
end

defp handle_extend_attrs(attrs, caller) do
block =
case Keyword.get(attrs, :meta) do
Expand Down Expand Up @@ -2382,6 +2430,9 @@ defmodule Absinthe.Schema.Notation do
defp recordable?([toplevel: true, extend: true], scope),
do: scope == :schema || scope == :extend

defp recordable?([toplevel: false, extend: true], scope),
do: scope == :extend

defp recordable?([toplevel: true], scope), do: scope == :schema
defp recordable?([toplevel: false], scope), do: scope != :schema

Expand Down
Loading

0 comments on commit a276684

Please sign in to comment.