Skip to content

Commit c52304e

Browse files
committed
Setup coordinates for objects, inputs, enums, fields, args, and enum values
1 parent 66cec34 commit c52304e

File tree

10 files changed

+223
-0
lines changed

10 files changed

+223
-0
lines changed

lib/absinthe/blueprint/schema/enum_type_definition.ex

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ defmodule Absinthe.Blueprint.Schema.EnumTypeDefinition do
77
defstruct [
88
:name,
99
:identifier,
10+
:coordinate,
1011
:description,
1112
:module,
1213
values: [],

lib/absinthe/blueprint/schema/enum_value_definition.ex

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ defmodule Absinthe.Blueprint.Schema.EnumValueDefinition do
88
:value,
99
:name,
1010
:identifier,
11+
:coordinate,
1112
deprecation: nil,
1213
directives: [],
1314
source_location: nil,

lib/absinthe/blueprint/schema/field_definition.ex

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ defmodule Absinthe.Blueprint.Schema.FieldDefinition do
77
defstruct [
88
:name,
99
:identifier,
10+
:coordinate,
1011
:type,
1112
:module,
1213
description: nil,

lib/absinthe/blueprint/schema/input_object_type_definition.ex

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ defmodule Absinthe.Blueprint.Schema.InputObjectTypeDefinition do
77
defstruct [
88
:identifier,
99
:name,
10+
:coordinate,
1011
:module,
1112
description: nil,
1213
fields: [],

lib/absinthe/blueprint/schema/input_value_definition.ex

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ defmodule Absinthe.Blueprint.Schema.InputValueDefinition do
66
defstruct [
77
:name,
88
:identifier,
9+
:coordinate,
910
:type,
1011
:module,
1112
# InputValueDefinitions can have different placements depending on Whether

lib/absinthe/blueprint/schema/object_type_definition.ex

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ defmodule Absinthe.Blueprint.Schema.ObjectTypeDefinition do
77
defstruct [
88
:name,
99
:identifier,
10+
:coordinate,
1011
:module,
1112
description: nil,
1213
interfaces: [],

lib/absinthe/blueprint/schema/schema_definition.ex

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ defmodule Absinthe.Blueprint.Schema.SchemaDefinition do
55

66
defstruct description: nil,
77
module: nil,
8+
coordinate: nil,
89
type_definitions: [],
910
directive_definitions: [],
1011
type_artifacts: [],
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
defmodule Absinthe.Phase.Schema.Coordinates do
2+
@moduledoc false
3+
# Schema phase that populates schema coordinates
4+
5+
use Absinthe.Phase
6+
7+
alias Absinthe.Blueprint.Schema
8+
alias Absinthe.Utils
9+
10+
def run(blueprint, _options \\ []) do
11+
{:ok, Absinthe.Blueprint.prewalk(blueprint, &process/1)}
12+
end
13+
14+
# Nodes without ancestors
15+
defp process(%Schema.SchemaDefinition{coordinate: nil} = node) do
16+
%{node | coordinate: "Schema"}
17+
end
18+
19+
defp process(%Schema.ObjectTypeDefinition{coordinate: nil} = node) do
20+
%{node | coordinate: node.name, fields: Enum.map(node.fields, &process(&1, node.name))}
21+
end
22+
23+
defp process(%Schema.InputObjectTypeDefinition{coordinate: nil} = node) do
24+
%{node | coordinate: node.name, fields: Enum.map(node.fields, &process(&1, node.name))}
25+
end
26+
27+
defp process(%Schema.EnumTypeDefinition{coordinate: nil} = node) do
28+
%{node | coordinate: node.name, values: Enum.map(node.values, &process(&1, node.name))}
29+
end
30+
31+
defp process(node), do: node
32+
33+
# Nodes with ancestors
34+
defp process(%Schema.FieldDefinition{coordinate: nil, name: name} = node, type_name) do
35+
coordinate = "#{type_name}.#{Utils.camelize(name, lower: true)}"
36+
arguments = Enum.map(node.arguments, &process(&1, coordinate))
37+
%{node | coordinate: coordinate, arguments: arguments}
38+
end
39+
40+
defp process(%Schema.InputValueDefinition{coordinate: nil, name: name} = node, coordinate) do
41+
%{node | coordinate: "#{coordinate}(#{Utils.camelize(name, lower: true)}:)"}
42+
end
43+
44+
defp process(%Schema.EnumValueDefinition{coordinate: nil, name: name} = node, enum_name) do
45+
%{node | coordinate: "#{enum_name}.#{name}"}
46+
end
47+
48+
defp process(node, _), do: node
49+
end

lib/absinthe/pipeline.ex

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -192,6 +192,7 @@ defmodule Absinthe.Pipeline do
192192
Phase.Schema.RegisterTriggers,
193193
Phase.Schema.MarkReferenced,
194194
Phase.Schema.ReformatDescriptions,
195+
Phase.Schema.Coordinates,
195196
# This phase is run again now after additional validations
196197
{Phase.Schema.Validation.Result, pass: :final},
197198
Phase.Schema.ImportPrototypeDirectives,
Lines changed: 166 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,166 @@
1+
defmodule Absinthe.Phase.Schema.CoordinatesTest do
2+
use Absinthe.Case, async: true
3+
4+
import Absinthe.Blueprint.Schema, only: [lookup_type: 2]
5+
6+
alias Absinthe.Phase.Schema.Coordinates
7+
8+
defmodule Schema do
9+
use Absinthe.Schema
10+
11+
enum :age_demographic do
12+
value :child
13+
value :young_adult
14+
value :adult
15+
end
16+
17+
enum :status do
18+
value :living
19+
value :not_living
20+
end
21+
22+
object :human_user do
23+
field :family_name, :string
24+
field :surname, :string
25+
field :age_demographic, :age_demographic
26+
field :status, :status
27+
end
28+
29+
input_object :name_input do
30+
field :family_name, :string
31+
field :surname, :string
32+
end
33+
34+
input_object :by do
35+
field :name, :name_input do
36+
arg(:living, :boolean)
37+
arg(:not_living, :boolean)
38+
end
39+
end
40+
41+
query do
42+
field :get_user, :human_user do
43+
arg(:by, :by)
44+
arg(:include_deceased, :boolean)
45+
end
46+
end
47+
end
48+
49+
setup_all do
50+
{:ok, blueprint} = Coordinates.run(Schema.__absinthe_blueprint__())
51+
[blueprint: blueprint]
52+
end
53+
54+
describe "run/2 for object types" do
55+
test "schema", %{blueprint: %{schema_definitions: [schema_definition]}} do
56+
assert %{coordinate: "Schema"} = schema_definition
57+
end
58+
59+
test "single word", %{blueprint: blueprint} do
60+
assert %{coordinate: "RootQueryType"} = lookup_type(blueprint, :query)
61+
end
62+
63+
test "multiple words", %{blueprint: blueprint} do
64+
assert %{coordinate: "HumanUser"} = lookup_type(blueprint, :human_user)
65+
end
66+
end
67+
68+
describe "run/2 for input types" do
69+
test "single word", %{blueprint: blueprint} do
70+
assert %{coordinate: "By"} = lookup_type(blueprint, :by)
71+
end
72+
73+
test "multiple words", %{blueprint: blueprint} do
74+
assert %{coordinate: "NameInput"} = lookup_type(blueprint, :name_input)
75+
end
76+
end
77+
78+
describe "run/2 for enum types" do
79+
test "single word", %{blueprint: blueprint} do
80+
assert %{coordinate: "Status"} = lookup_type(blueprint, :status)
81+
end
82+
83+
test "multiple words", %{blueprint: blueprint} do
84+
assert %{coordinate: "AgeDemographic"} = lookup_type(blueprint, :age_demographic)
85+
end
86+
end
87+
88+
describe "run/2 for enum values" do
89+
test "single word", %{blueprint: blueprint} do
90+
assert %{coordinate: "AgeDemographic.CHILD"} =
91+
lookup_enum_value(blueprint, :age_demographic, :child)
92+
end
93+
94+
test "multiple words", %{blueprint: blueprint} do
95+
assert %{coordinate: "AgeDemographic.YOUNG_ADULT"} =
96+
lookup_enum_value(blueprint, :age_demographic, :young_adult)
97+
end
98+
end
99+
100+
describe "run/2 for fields" do
101+
test "top level field", %{blueprint: blueprint} do
102+
assert %{coordinate: "RootQueryType.getUser"} = lookup_field(blueprint, :query, :get_user)
103+
end
104+
105+
test "single words", %{blueprint: blueprint} do
106+
assert %{coordinate: "HumanUser.surname"} =
107+
lookup_field(blueprint, :human_user, :surname)
108+
end
109+
110+
test "multiple words", %{blueprint: blueprint} do
111+
assert %{coordinate: "HumanUser.familyName"} =
112+
lookup_field(blueprint, :human_user, :family_name)
113+
end
114+
115+
test "single word on input type", %{blueprint: blueprint} do
116+
assert %{coordinate: "NameInput.surname"} = lookup_field(blueprint, :name_input, :surname)
117+
end
118+
119+
test "multiple words on input type", %{blueprint: blueprint} do
120+
assert %{coordinate: "NameInput.familyName"} =
121+
lookup_field(blueprint, :name_input, :family_name)
122+
end
123+
end
124+
125+
describe "run/2 for arguments" do
126+
test "single word", %{blueprint: blueprint} do
127+
assert %{coordinate: "RootQueryType.getUser(by:)"} =
128+
lookup_argument(blueprint, :query, :get_user, :by)
129+
end
130+
131+
test "multiple words", %{blueprint: blueprint} do
132+
assert %{coordinate: "RootQueryType.getUser(includeDeceased:)"} =
133+
lookup_argument(blueprint, :query, :get_user, :include_deceased)
134+
end
135+
136+
test "single word on input field", %{blueprint: blueprint} do
137+
assert %{coordinate: "By.name(living:)"} = lookup_argument(blueprint, :by, :name, :living)
138+
end
139+
140+
test "multiple words on input field", %{blueprint: blueprint} do
141+
assert %{coordinate: "By.name(notLiving:)"} =
142+
lookup_argument(blueprint, :by, :name, :not_living)
143+
end
144+
end
145+
146+
defp lookup_field(blueprint, type, field) do
147+
blueprint
148+
|> lookup_type(type)
149+
|> Map.get(:fields)
150+
|> Enum.find(&(&1.identifier == field))
151+
end
152+
153+
defp lookup_argument(blueprint, type, field, argument) do
154+
blueprint
155+
|> lookup_field(type, field)
156+
|> Map.get(:arguments)
157+
|> Enum.find(&(&1.identifier == argument))
158+
end
159+
160+
defp lookup_enum_value(blueprint, enum, value) do
161+
blueprint
162+
|> lookup_type(enum)
163+
|> Map.get(:values)
164+
|> Enum.find(&(&1.identifier == value))
165+
end
166+
end

0 commit comments

Comments
 (0)