Skip to content

Commit 65ce8cc

Browse files
jwaldripclaude
andcommitted
feat: add description support for operations and fragments
Implements the GraphQL September 2025 specification feature that allows descriptions on executable definitions (operations and fragments). Changes: - Add `description` field to Blueprint.Document.Operation struct - Add `description` field to Blueprint.Document.Fragment.Named struct - Add `description` field to Language.OperationDefinition struct - Add `description` field to Language.Fragment struct - Update parser to accept descriptions before operations and fragments - Update Blueprint.Draft conversion to preserve descriptions - Update SDL rendering to output descriptions for operations/fragments - Add comprehensive tests for the new functionality Specification: https://spec.graphql.org/September2025/#sec-Descriptions Reference: RFC absinthe-graphql#1170 Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
1 parent a035261 commit 65ce8cc

File tree

7 files changed

+608
-8
lines changed

7 files changed

+608
-8
lines changed

lib/absinthe/blueprint/document/fragment/named.ex

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ defmodule Absinthe.Blueprint.Document.Fragment.Named do
88
defstruct [
99
:name,
1010
:type_condition,
11+
:description,
1112
selections: [],
1213
directives: [],
1314
source_location: nil,
@@ -22,6 +23,7 @@ defmodule Absinthe.Blueprint.Document.Fragment.Named do
2223
directives: [Blueprint.Directive.t()],
2324
errors: [Absinthe.Phase.Error.t()],
2425
name: String.t(),
26+
description: nil | String.t(),
2527
selections: [Blueprint.Document.selection_t()],
2628
schema_node: nil | Absinthe.Type.t(),
2729
source_location: nil | Blueprint.SourceLocation.t(),

lib/absinthe/blueprint/document/operation.ex

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ defmodule Absinthe.Blueprint.Document.Operation do
77
defstruct [
88
:name,
99
:type,
10+
:description,
1011
current: false,
1112
selections: [],
1213
directives: [],
@@ -25,6 +26,7 @@ defmodule Absinthe.Blueprint.Document.Operation do
2526
@type t :: %__MODULE__{
2627
name: nil | String.t(),
2728
type: :query | :mutation | :subscription,
29+
description: nil | String.t(),
2830
current: boolean,
2931
directives: [Blueprint.Directive.t()],
3032
selections: [Blueprint.Document.selection_t()],

lib/absinthe/language/fragment.ex

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,15 @@ defmodule Absinthe.Language.Fragment do
44
alias Absinthe.{Blueprint, Language}
55

66
defstruct name: nil,
7+
description: nil,
78
type_condition: nil,
89
directives: [],
910
selection_set: nil,
1011
loc: %{line: nil}
1112

1213
@type t :: %__MODULE__{
1314
name: String.t(),
15+
description: nil | String.t(),
1416
type_condition: nil | Language.NamedType.t(),
1517
directives: [Language.Directive.t()],
1618
selection_set: Language.SelectionSet.t(),
@@ -21,6 +23,7 @@ defmodule Absinthe.Language.Fragment do
2123
def convert(node, doc) do
2224
%Blueprint.Document.Fragment.Named{
2325
name: node.name,
26+
description: node.description,
2427
type_condition: Blueprint.Draft.convert(node.type_condition, doc),
2528
selections: Blueprint.Draft.convert(node.selection_set.selections, doc),
2629
directives: Blueprint.Draft.convert(node.directives, doc),

lib/absinthe/language/operation_definition.ex

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ defmodule Absinthe.Language.OperationDefinition do
55

66
defstruct operation: nil,
77
name: nil,
8+
description: nil,
89
variable_definitions: [],
910
directives: [],
1011
selection_set: nil,
@@ -14,6 +15,7 @@ defmodule Absinthe.Language.OperationDefinition do
1415
@type t :: %__MODULE__{
1516
operation: :query | :mutation | :subscription,
1617
name: nil | String.t(),
18+
description: nil | String.t(),
1719
variable_definitions: [Language.VariableDefinition.t()],
1820
directives: [Language.Directive.t()],
1921
selection_set: Language.SelectionSet.t(),
@@ -26,6 +28,7 @@ defmodule Absinthe.Language.OperationDefinition do
2628
%Blueprint.Document.Operation{
2729
name: node.name,
2830
type: node.operation,
31+
description: node.description,
2932
directives: Absinthe.Blueprint.Draft.convert(node.directives, doc),
3033
variable_definitions: Blueprint.Draft.convert(node.variable_definitions, doc),
3134
selections: Blueprint.Draft.convert(node.selection_set.selections, doc),

lib/absinthe/language/render.ex

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -24,14 +24,17 @@ defmodule Absinthe.Language.Render do
2424
end
2525

2626
defp render(%Absinthe.Language.OperationDefinition{} = op) do
27-
if op.shorthand do
28-
concat(operation_definition(op), block(render_list(op.selection_set.selections)))
29-
else
30-
glue(
31-
concat([to_string(op.operation), operation_definition(op)]),
32-
block(render_list(op.selection_set.selections))
33-
)
34-
end
27+
doc =
28+
if op.shorthand do
29+
concat(operation_definition(op), block(render_list(op.selection_set.selections)))
30+
else
31+
glue(
32+
concat([to_string(op.operation), operation_definition(op)]),
33+
block(render_list(op.selection_set.selections))
34+
)
35+
end
36+
37+
description(doc, op.description)
3538
end
3639

3740
defp render(%Absinthe.Language.Field{} = field) do
@@ -133,6 +136,7 @@ defmodule Absinthe.Language.Render do
133136
directives(fragment.directives)
134137
])
135138
|> block(render_list(fragment.selection_set.selections))
139+
|> description(fragment.description)
136140
end
137141

138142
# Schema

src/absinthe_parser.yrl

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,9 @@ Definitions -> Definition : ['$1'].
3131
Definitions -> Definition Definitions : ['$1'|'$2'].
3232

3333
Definition -> OperationDefinition : '$1'.
34+
Definition -> DescriptionDefinition OperationDefinition : put_description('$2', '$1').
3435
Definition -> Fragment : '$1'.
36+
Definition -> DescriptionDefinition Fragment : put_description('$2', '$1').
3537
Definition -> TypeDefinition : '$1'.
3638

3739
OperationType -> 'query' : '$1'.

0 commit comments

Comments
 (0)