improvement!: No longer automagically derive types

This commit is contained in:
Zach Daniel 2024-04-01 15:27:43 -04:00
parent 78c5b6a3bb
commit 349086fbb8
21 changed files with 577 additions and 1567 deletions

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,74 @@
# Upgrading to 1.0
AshGraphql 1.0 is a major release that introduces 3.0 support as well as a few
breaking changes for AshGraphql itself.
## Automagic atom/union/map types are no more
Pre 1.0: AshGraphql automatically generated types for attributes/arguments that were atom/union/map types, giving them a name like `postStatus`, for an attribute `status` on a resource `post`. While convenient, this incurred _significant_ internal complexity, and had room for strange ambiguity. For example, if you had two actions, that each had an argument called `:status`, and that `:status` was an enum with different values, you would get a conflict at compile time due to conflicting type names.
### What you'll need to change
AshGraphql will now _only_ generate types for types defined using `Ash.Type.NewType` or `Ash.Type.Enum`. For example, if you had:
```elixir
attribute :post_status, :atom, constraints: [one_of: [:active, :archived]]
```
in 3.0 this attribute would display as a `:string`. To fix this, you would define an `Ash.Type.Enum`:
```elixir
defmodule MyApp.PostStatus do
use Ash.Type.Enum, values: [:active, :archived]
def graphql_type(_), do: :post_status
def graphql_input_type(_), do: :post_status
end
```
Then you could use it in your attribute:
```elixir
attribute :post_status, MyApp.PostStatus
```
The same goes for map types with the `:fields` constraint, as well as union types, except you must define those using `Ash.Type.NewType`. For example:
```elixir
attribute :scale, :union, constraints: [
types: [
whole: [
type: :integer
],
fractional: [
type: :decimal
]
]
]
```
Here you would get a compile error, indicating that we cannot determine a type for `:union`. To resolve this, you would define an `Ash.Type.NewType`, like so:
```elixir
defmodule MyApp.Scale do
use Ash.Type.NewType, subtype_of: :union, constraints: [
types: [
whole: [
type: :integer
],
fractional: [
type: :decimal
]
]
]
def graphql_type(_), do: :scale
def graphql_input_type(_), do: :scale
end
```
Then you could use it in your application like so:
```elixir
attribute :scale, MyApp.Scale
```

View file

@ -1,7 +1,6 @@
# Use Unions with GraphQL
By default, if a union is used in your resource in line, it will get a nice type generated for it based on the
resource/key name. Often, you'll want to define a union using `Ash.Type.NewType`. For example:
Unions must be defined with `Ash.Type.NewType`:
```elixir
defmodule MyApp.Armor do

View file

@ -87,22 +87,11 @@ defmodule AshGraphql do
for resource <- ash_resources do
resource
|> AshGraphql.Resource.get_auto_unions()
|> Enum.concat(resource |> AshGraphql.Resource.global_unions() |> Enum.map(&elem(&1, 1)))
|> AshGraphql.Resource.global_unions()
|> Enum.map(&elem(&1, 1))
|> Enum.map(fn attribute ->
if Ash.Type.NewType.new_type?(attribute.type) do
cond do
function_exported?(attribute.type, :graphql_type, 0) ->
attribute.type.graphql_type()
function_exported?(attribute.type, :graphql_type, 1) ->
attribute.type.graphql_type(attribute.constraints)
true ->
AshGraphql.Resource.atom_enum_type(resource, attribute.name)
end
else
AshGraphql.Resource.atom_enum_type(resource, attribute.name)
if function_exported?(attribute.type, :graphql_type, 1) do
attribute.type.graphql_type(attribute.constraints)
end
end)
|> Enum.uniq()
@ -252,7 +241,7 @@ defmodule AshGraphql do
{"DurationName", :duration_name}
type ->
graphql_type = type.graphql_type()
graphql_type = type.graphql_type([])
{graphql_type |> to_string() |> Macro.camelize(), graphql_type}
end
@ -305,23 +294,13 @@ defmodule AshGraphql do
resource
|> AshGraphql.Resource.global_unions()
|> Enum.flat_map(fn {type, attribute} ->
type_name =
if function_exported?(type, :graphql_type, 0) do
type.graphql_type()
else
type.graphql_type(attribute.constraints)
end
type_name = type.graphql_type(attribute.constraints)
input_type_name =
cond do
function_exported?(type, :graphql_input_type, 0) ->
type.graphql_input_type()
function_exported?(type, :graphql_input_type, 1) ->
type.graphql_input_type(attribute.constraints)
true ->
"#{type_name}_input"
if function_exported?(type, :graphql_input_type, 1) do
type.graphql_input_type(attribute.constraints)
else
"#{type_name}_input"
end
AshGraphql.Resource.union_type_definitions(
@ -580,8 +559,7 @@ defmodule AshGraphql do
defp union_type(type) do
if Ash.Type.NewType.new_type?(type) &&
Ash.Type.NewType.subtype_of(type) == Ash.Type.Union &&
(function_exported?(type, :graphql_type, 0) ||
function_exported?(type, :graphql_type, 1)) do
function_exported?(type, :graphql_type, 1) do
type
end
end

View file

@ -1991,15 +1991,8 @@ defmodule AshGraphql.Graphql.Resolver do
case original_type do
{type, constraints} ->
configured_type_name =
cond do
function_exported?(type, :graphql_type, 0) ->
type.graphql_type()
function_exported?(type, :graphql_type, 1) ->
type.graphql_type(constraints)
true ->
nil
if function_exported?(type, :graphql_type, 1) do
type.graphql_type(constraints)
end
unnested_unions =
@ -2026,8 +2019,7 @@ defmodule AshGraphql.Graphql.Resolver do
config[:type],
%Ash.Resource.Attribute{
name:
configured_type_name ||
AshGraphql.Resource.atom_enum_type(resource, field_name),
configured_type_name,
type: config[:type],
constraints: config[:constraints]
},

View file

@ -1710,14 +1710,12 @@ defmodule AshGraphql.Resource do
List.wrap(keyset_page_of(resource, schema)) ++
map_definitions(resource, schema, __ENV__) ++
enum_definitions(resource, schema, __ENV__) ++
union_definitions(resource, schema, __ENV__) ++
managed_relationship_definitions(resource, schema)
end
def no_graphql_types(resource, schema) do
map_definitions(resource, schema, __ENV__) ++
enum_definitions(resource, schema, __ENV__, true) ++
union_definitions(resource, schema, __ENV__) ++
enum_definitions(resource, schema, __ENV__) ++
managed_relationship_definitions(resource, schema)
end
@ -2774,48 +2772,18 @@ defmodule AshGraphql.Resource do
def map_definitions(resource, schema, env) do
if AshGraphql.Resource.Info.type(resource) do
resource
|> get_auto_maps()
|> global_maps()
|> Enum.flat_map(fn attribute ->
constraints = Ash.Type.NewType.constraints(attribute.type, attribute.constraints)
type_name =
if constraints[:fields] do
if Ash.Type.NewType.new_type?(attribute.type) do
cond do
function_exported?(attribute.type, :graphql_type, 0) ->
attribute.type.graphql_type()
function_exported?(attribute.type, :graphql_type, 1) ->
attribute.type.graphql_type(attribute.constraints)
true ->
map_type(resource, attribute.name)
end
else
map_type(resource, attribute.name)
end
else
nil
if function_exported?(attribute.type, :graphql_type, 1) do
attribute.type.graphql_type(attribute.constraints)
end
input_type_name =
if constraints[:fields] do
if Ash.Type.NewType.new_type?(attribute.type) do
cond do
function_exported?(attribute.type, :graphql_input_type, 0) ->
attribute.type.graphql_input_type()
function_exported?(attribute.type, :graphql_input_type, 1) ->
attribute.type.graphql_input_type(attribute.constraints)
true ->
map_type(resource, attribute.name, true)
end
else
map_type(resource, attribute.name, true)
end
else
nil
if function_exported?(attribute.type, :graphql_input_type, 1) do
attribute.type.graphql_input_type(attribute.constraints)
end
[
@ -3049,115 +3017,30 @@ defmodule AshGraphql.Resource do
end)
end
def enum_definitions(resource, schema, env, only_auto? \\ false) do
def enum_definitions(resource, schema, env) do
resource = Ash.Type.NewType.subtype_of(resource)
if AshGraphql.Resource.Info.type(resource) do
atom_enums =
resource
|> get_auto_enums()
|> Enum.flat_map(fn attribute ->
constraints = Ash.Type.NewType.constraints(attribute.type, attribute.constraints)
if AshGraphql.Resource.Info.type(resource) && AshGraphql.Resource.Info.derive_sort?(resource) do
sort_values = sort_values(resource)
type_name =
if constraints[:one_of] do
if Ash.Type.NewType.new_type?(attribute.type) do
cond do
function_exported?(attribute.type, :graphql_type, 0) ->
attribute.type.graphql_type()
function_exported?(attribute.type, :graphql_type, 1) ->
attribute.type.graphql_type(attribute.constraints)
true ->
atom_enum_type(resource, attribute.name)
end
else
atom_enum_type(resource, attribute.name)
end
end
additional_type_name =
if constraints[:one_of] && Ash.Type.NewType.new_type?(attribute.type) do
cond do
function_exported?(attribute.type, :graphql_input_type, 0) ->
attribute.type.graphql_input_type()
function_exported?(attribute.type, :graphql_input_type, 1) ->
attribute.type.graphql_input_type(attribute.constraints)
true ->
atom_enum_type(resource, attribute.name)
end
else
nil
end
[
type_name,
additional_type_name
]
|> Enum.filter(& &1)
|> Enum.map(fn type_name ->
%Absinthe.Blueprint.Schema.EnumTypeDefinition{
sort_order = %Absinthe.Blueprint.Schema.EnumTypeDefinition{
module: schema,
name: resource |> resource_sort_field_type() |> to_string() |> Macro.camelize(),
identifier: resource_sort_field_type(resource),
__reference__: ref(__ENV__),
values:
Enum.map(sort_values, fn {sort_value_alias, sort_value} ->
%Absinthe.Blueprint.Schema.EnumValueDefinition{
module: schema,
name: type_name |> to_string() |> Macro.camelize(),
values:
Enum.map(constraints[:one_of], fn value ->
%Absinthe.Blueprint.Schema.EnumValueDefinition{
module: schema,
identifier: value,
__reference__: AshGraphql.Resource.ref(env),
name: String.upcase(to_string(value)),
value: value
}
end),
identifier: type_name,
__reference__: ref(__ENV__)
identifier: sort_value_alias,
__reference__: AshGraphql.Resource.ref(env),
name: String.upcase(to_string(sort_value_alias)),
value: sort_value
}
end)
end)
}
if only_auto? || !AshGraphql.Resource.Info.derive_sort?(resource) do
atom_enums
else
sort_values = sort_values(resource)
sort_order = %Absinthe.Blueprint.Schema.EnumTypeDefinition{
module: schema,
name: resource |> resource_sort_field_type() |> to_string() |> Macro.camelize(),
identifier: resource_sort_field_type(resource),
__reference__: ref(__ENV__),
values:
Enum.map(sort_values, fn {sort_value_alias, sort_value} ->
%Absinthe.Blueprint.Schema.EnumValueDefinition{
module: schema,
identifier: sort_value_alias,
__reference__: AshGraphql.Resource.ref(env),
name: String.upcase(to_string(sort_value_alias)),
value: sort_value
}
end)
}
[sort_order | atom_enums]
end
else
[]
end
end
# sobelow_skip ["RCE.CodeModule", "DOS.BinToAtom", "DOS.StringToAtom"]
def union_definitions(resource, schema, env) do
if AshGraphql.Resource.Info.type(resource) do
resource
|> get_auto_unions()
|> Enum.flat_map(fn attribute ->
type_name = atom_enum_type(resource, attribute.name)
input_type_name = "#{atom_enum_type(resource, attribute.name)}_input"
union_type_definitions(resource, attribute, type_name, schema, env, input_type_name)
end)
[sort_order]
else
[]
end
@ -3292,7 +3175,7 @@ defmodule AshGraphql.Resource do
end
@doc false
def get_auto_maps(resource) do
def global_maps(resource) do
resource
|> AshGraphql.all_attributes_and_arguments([], false)
|> Enum.map(&unnest/1)
@ -3300,35 +3183,12 @@ defmodule AshGraphql.Resource do
|> Enum.uniq_by(& &1.name)
end
@doc false
def get_auto_enums(resource) do
resource
|> AshGraphql.all_attributes_and_arguments([], false)
|> Enum.map(&unnest/1)
|> Enum.filter(&(Ash.Type.NewType.subtype_of(&1.type) == Ash.Type.Atom))
|> Enum.uniq_by(& &1.name)
end
defp unnest(%{type: {:array, type}, constraints: constraints} = attribute) do
unnest(%{attribute | type: type, constraints: constraints[:items] || []})
end
defp unnest(other), do: other
@doc false
def get_auto_unions(resource) do
resource
|> AshGraphql.all_attributes_and_arguments()
|> Enum.map(fn attribute ->
unnest(attribute)
end)
|> Enum.reject(fn attribute ->
function_exported?(attribute.type, :graphql_type, 0) ||
function_exported?(attribute.type, :graphql_type, 1)
end)
|> Enum.filter(&(Ash.Type.NewType.subtype_of(&1.type) == Ash.Type.Union))
end
@doc false
def global_unions(resource) do
resource
@ -4201,15 +4061,10 @@ defmodule AshGraphql.Resource do
end
else
if Spark.implements_behaviour?(type, Ash.Type.Enum) do
cond do
function_exported?(type, :graphql_type, 0) ->
type.graphql_type()
function_exported?(type, :graphql_type, 1) ->
type.graphql_type(attribute.constraints)
true ->
:string
if function_exported?(type, :graphql_type, 1) do
type.graphql_type(attribute.constraints)
else
:string
end
else
function =
@ -4223,18 +4078,10 @@ defmodule AshGraphql.Resource do
function_exported?(type, function, 1) ->
apply(type, function, [constraints])
function_exported?(type, function, 0) ->
apply(type, function, [])
input? && Ash.Type.NewType.new_type?(type) &&
Ash.Type.NewType.subtype_of(type) == Ash.Type.Union &&
(function_exported?(type, :graphql_type, 0) ||
function_exported?(type, :graphql_type, 1)) ->
if function_exported?(type, :graphql_type, 0) do
:"#{type.graphql_type()}_input"
else
:"#{type.graphql_type(constraints)}_input"
end
function_exported?(type, :graphql_type, 1) ->
:"#{type.graphql_type(constraints)}_input"
true ->
if Ash.Type.NewType.new_type?(type) do
@ -4266,58 +4113,24 @@ defmodule AshGraphql.Resource do
defp get_specific_field_type(
Ash.Type.Atom,
%{constraints: constraints, name: name},
resource,
_,
_resource,
_input?
)
when not is_nil(resource) do
if is_list(constraints[:one_of]) && AshGraphql.Resource.Info.type(resource) do
atom_enum_type(resource, name)
else
:string
end
end
# sobelow_skip ["DOS.BinToAtom"]
defp get_specific_field_type(
Ash.Type.Union,
%{name: name},
resource,
input?
)
when not is_nil(resource) do
# same logic for naming a union currently
base_type_name = atom_enum_type(resource, name)
if input? do
:"#{base_type_name}_input"
else
base_type_name
end
) do
:string
end
defp get_specific_field_type(
Ash.Type.Map,
%{constraints: constraints, name: name},
resource,
input?
_attribute,
_resource,
_input?
) do
if is_list(constraints[:fields]) do
map_type(resource, name, input?)
else
Application.get_env(:ash_graphql, :json_type) || :json_string
end
Application.get_env(:ash_graphql, :json_type) || :json_string
end
defp get_specific_field_type(Ash.Type.Map, _, _, _),
do: Application.get_env(:ash_graphql, :json_type) || :json_string
defp get_specific_field_type(Ash.Type.Boolean, _, _, _), do: :boolean
defp get_specific_field_type(Ash.Type.Atom, _, _, _) do
:string
end
defp get_specific_field_type(Ash.Type.CiString, _, _, _), do: :string
defp get_specific_field_type(Ash.Type.Date, _, _, _), do: :date
defp get_specific_field_type(Ash.Type.Decimal, _, _, _), do: :decimal
@ -4328,13 +4141,13 @@ defmodule AshGraphql.Resource do
defp get_specific_field_type(Ash.Type.Term, _, _, _), do: :string
defp get_specific_field_type(Ash.Type.DateTime, _, _, _),
do: Application.get_env(:ash, :utc_datetime_type) || raise_datetime_error()
do: Application.get_env(:ash, :utc_datetime_type) || :datetime
defp get_specific_field_type(Ash.Type.UtcDatetime, _, _, _),
do: Application.get_env(:ash, :utc_datetime_type) || raise_datetime_error()
do: Application.get_env(:ash, :utc_datetime_type) || :datetime
defp get_specific_field_type(Ash.Type.UtcDatetimeUsec, _, _, _),
do: Application.get_env(:ash, :utc_datetime_type) || raise_datetime_error()
do: Application.get_env(:ash, :utc_datetime_type) || :datetime
defp get_specific_field_type(Ash.Type.NaiveDatetime, _, _, _), do: :naive_datetime
defp get_specific_field_type(Ash.Type.Time, _, _, _), do: :time
@ -4356,67 +4169,19 @@ defmodule AshGraphql.Resource do
raise """
Could not determine graphql field type for #{inspect(type)} on #{inspect(resource)}.#{attribute.name}
If this is a custom type, you can add `def graphql_type/1` to your type to define the graphql type.
If this is not your type, you will need to use `types` or `attribute_types` or `attribute_input_types`
to configure the type for any field using this type. If this is an `Ash.Type.NewType`, you may need to define
`graphql_type` and `graphql_input_type`s for it.
If this is an `Ash.Type.Enum` or a custom type, you can add `def graphql_type/1` or `def graphql_input_type/1`
to your type. This does not *define* the type, but tells us what type to use for it, so you may need
to add a type to your absinthe schema if it does not map to a built in absinthe data type.
## Ash.Type.NewType
The exception to the above are special instances of `Ash.Type.NewType`. If you have an `Ash.Type.NewType`,
that is a subset of `:union` or `:map`(with the `:fields` constraint), AshGraphql will define that type for you.
If you are seeing this message, it likely means that you are missing `def graphql_type/1` or `def graphql_input_type/1`
on your type definition.
"""
end
defp raise_datetime_error do
raise """
No type configured for utc_datetimes!
The existing default of using `:naive_datetime` for `:utc_datetime` and `:utc_datetime_usec` is being deprecated.
To prevent accidental API breakages, we are requiring that you configure your selected type for these, via
# This was the previous default, so use this if you want to ensure no unintended
# change in your API, although switching to `:datetime` eventually is suggested.
config :ash, :utc_datetime_type, :naive_datetime
or
config :ash, :utc_datetime_type, :datetime
When the 1.0 version of ash_graphql is released, the default will be changed to `:datetime`, and this error message will
no longer be shown (but any configuration set will be retained indefinitely).
"""
end
# sobelow_skip ["DOS.StringToAtom"]
@doc false
def atom_enum_type(resource, attribute_name) do
field_names = AshGraphql.Resource.Info.field_names(resource)
resource
|> AshGraphql.Resource.Info.type()
|> to_string()
|> Kernel.<>("_")
|> Kernel.<>(to_string(field_names[attribute_name] || attribute_name))
|> String.to_atom()
end
# sobelow_skip ["DOS.StringToAtom", "DOS.BinToAtom"]
@doc false
def map_type(resource, attribute_name, input? \\ false) do
field_names = AshGraphql.Resource.Info.field_names(resource)
name =
resource
|> AshGraphql.Resource.Info.type()
|> to_string()
|> Kernel.<>("_")
|> Kernel.<>(to_string(field_names[attribute_name] || attribute_name))
|> String.to_atom()
if input? do
:"#{name}_input"
else
name
end
end
def primary_key_get_query(resource) do
# Find the get query with no identities, i.e. the one that uses the primary key
resource

View file

@ -42,204 +42,6 @@ defmodule AshGraphql.AttributeTest do
assert author_id_field["type"]["name"] == "ID"
end
test "atom attribute with one_of constraints has enums automatically generated" do
{:ok, %{data: data}} =
"""
query {
__type(name: "PostVisibility") {
enumValues {
name
}
}
}
"""
|> Absinthe.run(AshGraphql.Test.Schema)
assert data["__type"]
end
test "atom attribute with one_of constraints uses enum for inputs" do
{:ok, %{data: data}} =
"""
query {
__type(name: "CreatePostInput") {
inputFields {
name
type {
kind
name
ofType {
kind
name
}
}
}
}
}
"""
|> Absinthe.run(AshGraphql.Test.Schema)
visibility_field =
data["__type"]["inputFields"]
|> Enum.find(fn field -> field["name"] == "visibility" end)
assert visibility_field["type"]["kind"] == "ENUM"
end
test "map attribute with field constraints get their own type" do
{:ok, %{data: data}} =
"""
query {
__type(name: "MapTypes") {
fields {
name
type {
kind
name
ofType {
kind
name
}
}
}
}
}
"""
|> Absinthe.run(AshGraphql.Test.Schema)
fields = data["__type"]["fields"]
attributes_field =
fields
|> Enum.find(fn field -> field["name"] == "attributes" end)
values_field =
fields
|> Enum.find(fn field -> field["name"] == "values" end)
assert attributes_field == %{
"name" => "attributes",
"type" => %{
"kind" => "NON_NULL",
"name" => nil,
"ofType" => %{"kind" => "OBJECT", "name" => "MapTypesAttributes"}
}
}
assert values_field == %{
"name" => "values",
"type" => %{"kind" => "OBJECT", "name" => "ConstrainedMap", "ofType" => nil}
}
end
test "map attribute with field constraints use input objects for inputs" do
{:ok, %{data: data}} =
"""
query {
__type(name: "MapTypesAttributesInput") {
inputFields {
name
type {
kind
name
ofType {
kind
name
}
}
}
}
}
"""
|> Absinthe.run(AshGraphql.Test.Schema)
foo_field =
data["__type"]["inputFields"]
|> Enum.find(fn field -> field["name"] == "foo" end)
# non null field
assert foo_field["type"]["kind"] == "NON_NULL"
assert foo_field["type"]["ofType"]["kind"] == "SCALAR"
assert foo_field["type"]["ofType"]["name"] == "String"
bar_field =
data["__type"]["inputFields"]
|> Enum.find(fn field -> field["name"] == "bar" end)
assert bar_field["type"]["kind"] == "SCALAR"
assert bar_field["type"]["name"] == "Int"
baz_field =
data["__type"]["inputFields"]
|> Enum.find(fn field -> field["name"] == "baz" end)
assert baz_field["type"]["kind"] == "SCALAR"
assert baz_field["type"]["name"] == "JsonString"
end
test "map arguments with constraints create an input object" do
assert {:ok,
%{
data: %{
"__type" => %{
"inputFields" => [
%{
"name" => "attributes",
"type" => %{
"kind" => "INPUT_OBJECT",
"name" => "MapTypesAttributesInput",
"ofType" => nil
}
},
%{
"name" => "inlineValues",
"type" => %{
"kind" => "INPUT_OBJECT",
"name" => "MapTypesInlineValuesInput",
"ofType" => nil
}
},
%{
"name" => "jsonMap",
"type" => %{
"kind" => "SCALAR",
"name" => "JsonString",
"ofType" => nil
}
},
%{
"name" => "values",
"type" => %{
"kind" => "INPUT_OBJECT",
"name" => "ConstrainedMapInput",
"ofType" => nil
}
}
]
}
}
}} =
"""
query {
__type(name: "InlineUpdateMapTypesInput") {
inputFields {
name
type {
kind
name
ofType {
kind
name
}
}
}
}
}
"""
|> Absinthe.run(AshGraphql.Test.Schema)
end
test "nested maps with constraints create types for nested maps" do
assert {:ok,
%{
@ -337,62 +139,4 @@ defmodule AshGraphql.AttributeTest do
"""
|> Absinthe.run(AshGraphql.Test.Schema)
end
test "map subtypes with constraints used as arguments use the subtype input object" do
assert {:ok,
%{
data: %{
"__type" => %{
"inputFields" => [
%{
"name" => "attributes",
"type" => %{
"kind" => "INPUT_OBJECT",
"name" => "MapTypesAttributesInput",
"ofType" => nil
}
},
%{
"name" => "jsonMap",
"type" => %{"kind" => "SCALAR", "name" => "JsonString", "ofType" => nil}
},
%{
"name" => "moduleValues",
"type" => %{
"kind" => "INPUT_OBJECT",
"name" => "ConstrainedMapInput",
"ofType" => nil
}
},
%{
"name" => "values",
"type" => %{
"kind" => "INPUT_OBJECT",
"name" => "ConstrainedMapInput",
"ofType" => nil
}
}
]
}
}
}} =
"""
query {
__type(name: "ModuleUpdateMapTypesInput") {
inputFields {
name
type {
kind
name
ofType {
kind
name
}
}
}
}
}
"""
|> Absinthe.run(AshGraphql.Test.Schema)
end
end

View file

@ -104,10 +104,10 @@ defmodule AshGraphql.CreateTest do
result{
text1
simpleUnion {
... on PostSimpleUnionString {
... on SimpleUnionString {
value
}
... on PostSimpleUnionInt {
... on SimpleUnionInt {
value
}
}
@ -153,13 +153,13 @@ defmodule AshGraphql.CreateTest do
simpleCreatePost(input: $input) {
result{
text1
embedUnion {
... on PostEmbedUnionFoo {
embedUnionNewType {
... on EmbedUnionNewTypeFoo {
value {
foo
}
}
... on PostEmbedUnionBar {
... on EmbedUnionNewTypeBar {
value {
bar
}
@ -176,7 +176,7 @@ defmodule AshGraphql.CreateTest do
variables: %{
"input" => %{
"text1" => "foo",
"embedUnion" => %{
"embedUnionNewType" => %{
"foo" => %{
"foo" => "10"
}
@ -193,7 +193,7 @@ defmodule AshGraphql.CreateTest do
data: %{
"simpleCreatePost" => %{
"result" => %{
"embedUnion" => %{
"embedUnionNewType" => %{
"value" => %{
"foo" => "10"
}
@ -212,12 +212,12 @@ defmodule AshGraphql.CreateTest do
result{
text1
embedUnionNewType {
... on FooBarFoo {
... on EmbedUnionNewTypeFoo {
value {
foo
}
}
... on FooBarBar {
... on EmbedUnionNewTypeBar {
value {
bar
}
@ -718,44 +718,6 @@ defmodule AshGraphql.CreateTest do
} = result
end
test "enum newtypes can be written to" do
resp =
"""
mutation CreatePost($input: CreatePostInput) {
createPost(input: $input) {
result{
text
enumNewType
}
errors{
message
}
}
}
"""
|> Absinthe.run(AshGraphql.Test.Schema,
variables: %{
"input" => %{
"text" => "foobar",
"confirmation" => "foobar",
"enumNewType" => "BIZ"
}
}
)
assert {:ok, result} = resp
refute Map.has_key?(result, :errors)
assert %{
data: %{
"createPost" => %{
"result" => %{"text" => "foobar", "enumNewType" => "BIZ"}
}
}
} = result
end
test "string newtypes can be written to" do
resp =
"""

View file

@ -53,10 +53,10 @@ defmodule AshGraphql.ReadTest do
postLibrary {
text
simpleUnion {
... on PostSimpleUnionString {
... on SimpleUnionString {
value
}
... on PostSimpleUnionInt {
... on SimpleUnionInt {
value
}
}
@ -886,7 +886,7 @@ defmodule AshGraphql.ReadTest do
AshGraphql.Test.Post
|> Ash.Changeset.for_create(:create,
text: "a",
embed_union: %{type: :foo, foo: "fred"},
embed_union_new_type: %{type: :foo, foo: "fred"},
published: true
)
|> Ash.create!()
@ -894,7 +894,7 @@ defmodule AshGraphql.ReadTest do
AshGraphql.Test.Post
|> Ash.Changeset.for_create(:create,
text: "b",
embed_union: %{type: :bar, bar: "george"},
embed_union_new_type: %{type: :bar, bar: "george"},
published: true
)
|> Ash.create!()
@ -903,13 +903,13 @@ defmodule AshGraphql.ReadTest do
"""
query postLibrary {
postLibrary(sort: {field: TEXT}) {
embedUnion{
...on PostEmbedUnionFoo {
embedUnionNewType{
...on EmbedUnionNewTypeFoo {
value {
alwaysNil
}
}
...on PostEmbedUnionBar {
...on EmbedUnionNewTypeBar {
value {
alwaysFalse
}
@ -928,14 +928,14 @@ defmodule AshGraphql.ReadTest do
data: %{
"postLibrary" => [
%{
"embedUnion" => %{
"embedUnionNewType" => %{
"value" => %{
"alwaysNil" => nil
}
}
},
%{
"embedUnion" => %{
"embedUnionNewType" => %{
"value" => %{
"alwaysFalse" => false
}

View file

@ -24,6 +24,6 @@ defmodule AshGraphql.Test.ConstrainedMap do
]
]
def graphql_type, do: :constrained_map
def graphql_input_type, do: :constrained_map_input
def graphql_type(_), do: :constrained_map
def graphql_input_type(_), do: :constrained_map_input
end

View file

@ -6,5 +6,5 @@ defmodule AshGraphql.Test.DoubleRelType do
:second
]
def graphql_type, do: :double_rel_type
def graphql_type(_), do: :double_rel_type
end

View file

@ -8,26 +8,6 @@ defmodule AshGraphql.Test.MapTypes do
attributes do
uuid_primary_key(:id)
attribute :attributes, :map do
constraints(
fields: [
foo: [
type: :string,
allow_nil?: false
],
bar: [
type: :integer
],
baz: [
type: :map
]
]
)
allow_nil? false
public?(true)
end
attribute(:json_map, :map, public?: true)
attribute :values, AshGraphql.Test.ConstrainedMap do
@ -40,22 +20,6 @@ defmodule AshGraphql.Test.MapTypes do
defaults([:create, :read, :update, :destroy])
update :inline do
argument :inline_values, :map do
constraints(
fields: [
foo: [
type: :string,
allow_nil?: false
],
bar: [
type: :integer
]
]
)
end
end
update :module do
argument(:module_values, AshGraphql.Test.ConstrainedMap)
end
@ -69,7 +33,6 @@ defmodule AshGraphql.Test.MapTypes do
end
mutations do
update :inline_update_map_types, :inline
update :module_update_map_types, :module
end
end

View file

@ -2,5 +2,5 @@ defmodule AshGraphql.Test.NestedEnum do
@moduledoc false
use Ash.Type.Enum, values: [:foo, :bar]
def graphql_type, do: :nested_enum
def graphql_type(_), do: :nested_enum
end

View file

@ -346,47 +346,16 @@ defmodule AshGraphql.Test.Post do
attribute(:text2, :string, public?: true)
attribute(:visibility, :atom, constraints: [one_of: [:public, :private]], public?: true)
attribute(:simple_union, :union,
constraints: [
types: [
int: [
type: :integer
],
string: [
type: :string
]
]
],
public?: true
)
attribute(:simple_union, AshGraphql.Test.Types.SimpleUnion, public?: true)
attribute(:embed_foo, Foo, public?: true)
attribute(:embed_union, :union,
constraints: [
types: [
foo: [
type: Foo,
tag: :type,
tag_value: :foo
],
bar: [
type: Bar,
tag: :type,
tag_value: :bar
]
]
],
public?: true
)
attribute(:embed_union_new_type_list, {:array, AshGraphql.Types.EmbedUnionNewTypeUnnested},
public?: true
)
attribute(:embed_union_new_type, AshGraphql.Types.EmbedUnionNewType, public?: true)
attribute(:embed_union_unnested, AshGraphql.Types.EmbedUnionNewTypeUnnested, public?: true)
attribute(:enum_new_type, AshGraphql.Types.EnumNewType, public?: true)
attribute(:string_new_type, AshGraphql.Types.StringNewType, public?: true)
attribute :required_string, :string do

View file

@ -17,5 +17,5 @@ defmodule AshGraphql.Types.EmbedUnionNewType do
]
]
def graphql_type, do: :foo_bar
def graphql_type(_), do: :embed_union_new_type
end

View file

@ -17,7 +17,7 @@ defmodule AshGraphql.Types.EmbedUnionNewTypeUnnested do
]
]
def graphql_type, do: :foo_bar_unnested
def graphql_type(_), do: :foo_bar_unnested
def graphql_unnested_unions(_), do: [:foo, :bar]
end

View file

@ -1,6 +1,6 @@
defmodule AshGraphql.Types.EnumNewType do
@moduledoc false
use Ash.Type.NewType, subtype_of: :atom, constraints: [one_of: [:biz, :buz]]
use Ash.Type.Enum, values: [:biz, :buz]
def graphql_type, do: :biz_buz
def graphql_type(_), do: :biz_buz
end

View file

@ -6,5 +6,5 @@ defmodule AshGraphql.Test.EnumWithAshDescription do
buzz: "A buzz"
]
def graphql_type, do: :enum_with_ash_description
def graphql_type(_), do: :enum_with_ash_description
end

View file

@ -7,7 +7,7 @@ defmodule AshGraphql.Test.EnumWithAshGraphqlDescription do
no_description: "And also this"
]
def graphql_type, do: :enum_with_ash_graphql_description
def graphql_type(_), do: :enum_with_ash_graphql_description
def graphql_describe_enum_value(:foo), do: "A foo"
def graphql_describe_enum_value(:bar), do: "A bar"

View file

@ -0,0 +1,18 @@
defmodule AshGraphql.Test.Types.SimpleUnion do
@moduledoc false
use Ash.Type.NewType, subtype_of: :union, constraints: [
types: [
int: [
type: :integer
],
string: [
type: :string
]
]
]
use AshGraphql.Type
@impl AshGraphql.Type
def graphql_type(_), do: :simple_union
end

View file

@ -2,5 +2,5 @@ defmodule AshGraphql.Test.StatusEnum do
@moduledoc false
use Ash.Type.Enum, values: [:open, :closed]
def graphql_type, do: :status_enum
def graphql_type(_), do: :status_enum
end