mirror of
https://github.com/ash-project/ash_graphql.git
synced 2024-09-20 05:13:33 +12:00
parent
3a11d988ab
commit
9cc9da0f2e
3 changed files with 163 additions and 83 deletions
|
@ -788,18 +788,19 @@ defmodule AshGraphql.Graphql.Resolver do
|
|||
case Map.fetch(args, :sort) do
|
||||
{:ok, sort} ->
|
||||
keyword_sort =
|
||||
Enum.map(sort, fn %{order: order, field: field} ->
|
||||
{field, order}
|
||||
Enum.map(sort, fn %{order: order, field: field} = input ->
|
||||
case Ash.Resource.Info.calculation(resource, field) do
|
||||
%{arguments: [_ | _]} ->
|
||||
input_name = String.to_existing_atom("#{field}_input")
|
||||
|
||||
{field, {order, input[input_name] || %{}}}
|
||||
|
||||
_ ->
|
||||
{field, order}
|
||||
end
|
||||
end)
|
||||
|
||||
fields =
|
||||
keyword_sort
|
||||
|> Keyword.keys()
|
||||
|> Enum.filter(&Ash.Resource.Info.public_aggregate(resource, &1))
|
||||
|
||||
query
|
||||
|> Ash.Query.load(fields)
|
||||
|> Ash.Query.sort(keyword_sort)
|
||||
Ash.Query.sort(query, keyword_sort)
|
||||
|
||||
_ ->
|
||||
query
|
||||
|
|
|
@ -789,7 +789,7 @@ defmodule AshGraphql.Resource do
|
|||
|> maybe_wrap_non_null(argument_required?(argument))
|
||||
|
||||
%Absinthe.Blueprint.Schema.FieldDefinition{
|
||||
identifier: argument.name,
|
||||
identifier: name,
|
||||
module: schema,
|
||||
name: to_string(name),
|
||||
type: type,
|
||||
|
@ -1788,25 +1788,26 @@ defmodule AshGraphql.Resource do
|
|||
|
||||
_ ->
|
||||
%Absinthe.Blueprint.Schema.InputObjectTypeDefinition{
|
||||
fields: [
|
||||
%Absinthe.Blueprint.Schema.FieldDefinition{
|
||||
identifier: :order,
|
||||
module: schema,
|
||||
name: "order",
|
||||
default_value: :asc,
|
||||
type: :sort_order,
|
||||
__reference__: ref(__ENV__)
|
||||
},
|
||||
%Absinthe.Blueprint.Schema.FieldDefinition{
|
||||
identifier: :field,
|
||||
module: schema,
|
||||
name: "field",
|
||||
type: %Absinthe.Blueprint.TypeReference.NonNull{
|
||||
of_type: resource_sort_field_type(resource)
|
||||
fields:
|
||||
[
|
||||
%Absinthe.Blueprint.Schema.FieldDefinition{
|
||||
identifier: :order,
|
||||
module: schema,
|
||||
name: "order",
|
||||
default_value: :asc,
|
||||
type: :sort_order,
|
||||
__reference__: ref(__ENV__)
|
||||
},
|
||||
__reference__: ref(__ENV__)
|
||||
}
|
||||
],
|
||||
%Absinthe.Blueprint.Schema.FieldDefinition{
|
||||
identifier: :field,
|
||||
module: schema,
|
||||
name: "field",
|
||||
type: %Absinthe.Blueprint.TypeReference.NonNull{
|
||||
of_type: resource_sort_field_type(resource)
|
||||
},
|
||||
__reference__: ref(__ENV__)
|
||||
}
|
||||
] ++ calc_input_fields(resource, schema),
|
||||
identifier: resource_sort_type(resource),
|
||||
module: schema,
|
||||
name: resource |> resource_sort_type() |> to_string() |> Macro.camelize(),
|
||||
|
@ -1818,6 +1819,43 @@ defmodule AshGraphql.Resource do
|
|||
end
|
||||
end
|
||||
|
||||
# sobelow_skip ["DOS.StringToAtom"]
|
||||
defp calc_input_fields(resource, schema) do
|
||||
calcs =
|
||||
resource
|
||||
|> Ash.Resource.Info.public_calculations()
|
||||
|> Enum.reject(fn
|
||||
%{type: {:array, _}} ->
|
||||
true
|
||||
|
||||
calc ->
|
||||
Ash.Type.embedded_type?(calc.type) || Enum.empty?(calc.arguments)
|
||||
end)
|
||||
|
||||
field_names = AshGraphql.Resource.Info.field_names(resource)
|
||||
|
||||
Enum.map(calcs, fn calc ->
|
||||
input_name = "#{field_names[calc.name] || calc.name}_input"
|
||||
|
||||
%Absinthe.Blueprint.Schema.FieldDefinition{
|
||||
identifier: String.to_atom("#{calc.name}_input"),
|
||||
module: schema,
|
||||
name: input_name,
|
||||
type: calc_input_type(calc.name, resource),
|
||||
__reference__: ref(__ENV__)
|
||||
}
|
||||
end)
|
||||
end
|
||||
|
||||
# sobelow_skip ["DOS.StringToAtom"]
|
||||
defp calc_input_type(calc, resource) do
|
||||
field_names = AshGraphql.Resource.Info.field_names(resource)
|
||||
|
||||
String.to_atom(
|
||||
"#{AshGraphql.Resource.Info.type(resource)}_#{field_names[calc] || calc}_input"
|
||||
)
|
||||
end
|
||||
|
||||
defp filter_input(resource, schema) do
|
||||
case resource_filter_fields(resource, schema) do
|
||||
[] ->
|
||||
|
@ -1838,11 +1876,9 @@ defmodule AshGraphql.Resource do
|
|||
defp calculation_input(resource, schema) do
|
||||
resource
|
||||
|> Ash.Resource.Info.public_calculations()
|
||||
|> Enum.filter(fn %{calculation: {module, _}} ->
|
||||
|> Enum.flat_map(fn %{calculation: {module, _}} = calculation ->
|
||||
Code.ensure_compiled(module)
|
||||
:erlang.function_exported(module, :expression, 2)
|
||||
end)
|
||||
|> Enum.flat_map(fn calculation ->
|
||||
filterable? = :erlang.function_exported(module, :expression, 2)
|
||||
field_type = calculation_type(calculation, resource)
|
||||
|
||||
arguments = calculation_args(calculation, resource, schema)
|
||||
|
@ -1864,56 +1900,54 @@ defmodule AshGraphql.Resource do
|
|||
)
|
||||
)
|
||||
|
||||
filter_input = %Absinthe.Blueprint.Schema.InputObjectTypeDefinition{
|
||||
input = %Absinthe.Blueprint.Schema.InputObjectTypeDefinition{
|
||||
fields: arguments,
|
||||
identifier:
|
||||
String.to_atom(
|
||||
to_string(calculation_filter_field_type(resource, calculation)) <> "_input"
|
||||
),
|
||||
identifier: String.to_atom(to_string(calc_input_type(calculation.name, resource))),
|
||||
module: schema,
|
||||
name:
|
||||
Macro.camelize(
|
||||
to_string(calculation_filter_field_type(resource, calculation)) <> "_input"
|
||||
),
|
||||
name: Macro.camelize(to_string(calc_input_type(calculation.name, resource))),
|
||||
__reference__: ref(__ENV__)
|
||||
}
|
||||
|
||||
filter_input_field = %Absinthe.Blueprint.Schema.FieldDefinition{
|
||||
identifier: :input,
|
||||
module: schema,
|
||||
name: "input",
|
||||
type:
|
||||
String.to_atom(
|
||||
to_string(calculation_filter_field_type(resource, calculation)) <> "_input"
|
||||
),
|
||||
__reference__: ref(__ENV__)
|
||||
}
|
||||
types =
|
||||
if Enum.empty?(arguments) do
|
||||
[]
|
||||
else
|
||||
[input]
|
||||
end
|
||||
|
||||
if Enum.empty?(arguments) do
|
||||
type_def = %Absinthe.Blueprint.Schema.InputObjectTypeDefinition{
|
||||
fields: filter_fields,
|
||||
identifier: calculation_filter_field_type(resource, calculation),
|
||||
module: schema,
|
||||
name: Macro.camelize(to_string(calculation_filter_field_type(resource, calculation))),
|
||||
__reference__: ref(__ENV__)
|
||||
}
|
||||
if filterable? do
|
||||
type_def =
|
||||
if Enum.empty?(arguments) do
|
||||
%Absinthe.Blueprint.Schema.InputObjectTypeDefinition{
|
||||
fields: filter_fields,
|
||||
identifier: calculation_filter_field_type(resource, calculation),
|
||||
module: schema,
|
||||
name:
|
||||
Macro.camelize(to_string(calculation_filter_field_type(resource, calculation))),
|
||||
__reference__: ref(__ENV__)
|
||||
}
|
||||
else
|
||||
filter_input_field = %Absinthe.Blueprint.Schema.FieldDefinition{
|
||||
identifier: :input,
|
||||
module: schema,
|
||||
name: "input",
|
||||
type: String.to_atom(to_string(calc_input_type(calculation.name, resource))),
|
||||
__reference__: ref(__ENV__)
|
||||
}
|
||||
|
||||
[
|
||||
type_def
|
||||
]
|
||||
%Absinthe.Blueprint.Schema.InputObjectTypeDefinition{
|
||||
fields: [filter_input_field | filter_fields],
|
||||
identifier: calculation_filter_field_type(resource, calculation),
|
||||
module: schema,
|
||||
name:
|
||||
Macro.camelize(to_string(calculation_filter_field_type(resource, calculation))),
|
||||
__reference__: ref(__ENV__)
|
||||
}
|
||||
end
|
||||
|
||||
[type_def | types]
|
||||
else
|
||||
type_def = %Absinthe.Blueprint.Schema.InputObjectTypeDefinition{
|
||||
fields: [filter_input_field | filter_fields],
|
||||
identifier: calculation_filter_field_type(resource, calculation),
|
||||
module: schema,
|
||||
name: Macro.camelize(to_string(calculation_filter_field_type(resource, calculation))),
|
||||
__reference__: ref(__ENV__)
|
||||
}
|
||||
|
||||
[
|
||||
filter_input,
|
||||
type_def
|
||||
]
|
||||
types
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
@ -2091,12 +2125,12 @@ defmodule AshGraphql.Resource do
|
|||
identifier: resource_sort_field_type(resource),
|
||||
__reference__: ref(__ENV__),
|
||||
values:
|
||||
Enum.map(sort_values, fn sort_value ->
|
||||
Enum.map(sort_values, fn {sort_value_alias, sort_value} ->
|
||||
%Absinthe.Blueprint.Schema.EnumValueDefinition{
|
||||
module: schema,
|
||||
identifier: sort_value,
|
||||
identifier: sort_value_alias,
|
||||
__reference__: AshGraphql.Resource.ref(env),
|
||||
name: String.upcase(to_string(sort_value)),
|
||||
name: String.upcase(to_string(sort_value_alias)),
|
||||
value: sort_value
|
||||
}
|
||||
end)
|
||||
|
@ -2164,12 +2198,25 @@ defmodule AshGraphql.Resource do
|
|||
end)
|
||||
|> Enum.map(& &1.name)
|
||||
|
||||
calculation_sort_values =
|
||||
resource
|
||||
|> Ash.Resource.Info.public_calculations()
|
||||
|> Enum.reject(fn
|
||||
%{type: {:array, _}} ->
|
||||
true
|
||||
|
||||
attribute ->
|
||||
Ash.Type.embedded_type?(attribute.type)
|
||||
end)
|
||||
|> Enum.map(& &1.name)
|
||||
|
||||
attribute_sort_values
|
||||
|> Enum.concat(aggregate_sort_values)
|
||||
|> Enum.map(fn name ->
|
||||
field_names[name] || name
|
||||
end)
|
||||
|> Enum.concat(calculation_sort_values)
|
||||
|> Enum.uniq()
|
||||
|> Enum.map(fn name ->
|
||||
{field_names[name] || name, name}
|
||||
end)
|
||||
end
|
||||
|
||||
# sobelow_skip ["DOS.StringToAtom"]
|
||||
|
@ -2687,7 +2734,7 @@ defmodule AshGraphql.Resource do
|
|||
raise "Cannot construct an input type for #{inspect(type)}"
|
||||
end
|
||||
|
||||
AshGraphql.Resource.Info.type(resource)
|
||||
AshGraphql.Resource.Info.type(type)
|
||||
else
|
||||
if Ash.Type.embedded_type?(type) do
|
||||
if input? do
|
||||
|
@ -2733,7 +2780,8 @@ defmodule AshGraphql.Resource do
|
|||
Ash.Type.Atom,
|
||||
%Ash.Resource.Attribute{constraints: constraints, name: name},
|
||||
resource
|
||||
) do
|
||||
)
|
||||
when resource do
|
||||
if is_list(constraints[:one_of]) do
|
||||
atom_enum_type(resource, name)
|
||||
else
|
||||
|
|
|
@ -269,6 +269,37 @@ defmodule AshGraphql.ReadTest do
|
|||
assert %{data: %{"postLibrary" => [%{"foo" => "1foo2", "bar" => "1bar2"}]}} = result
|
||||
end
|
||||
|
||||
test "the same calculation can be sorted on twice with different arguments via aliases" do
|
||||
AshGraphql.Test.Post
|
||||
|> Ash.Changeset.for_create(:create, text: "bar", text1: "1", text2: "2", published: true)
|
||||
|> AshGraphql.Test.Api.create!()
|
||||
|
||||
AshGraphql.Test.Post
|
||||
|> Ash.Changeset.for_create(:create, text: "bar", text1: "1", text2: "2", published: true)
|
||||
|> AshGraphql.Test.Api.create!()
|
||||
|
||||
resp =
|
||||
"""
|
||||
query PostLibrary($published: Boolean) {
|
||||
postLibrary(published: $published, sort: [{field: TEXT1_AND2, order: DESC, text1And2Input: {separator: "a"}}, {field: TEXT1_AND2, order: DESC, text1And2Input: {separator: "b"}}]) {
|
||||
a: text1And2(separator: "a")
|
||||
b: text1And2(separator: "b")
|
||||
}
|
||||
}
|
||||
"""
|
||||
|> Absinthe.run(AshGraphql.Test.Schema)
|
||||
|
||||
assert {:ok, result} = resp
|
||||
|
||||
refute Map.has_key?(result, :errors)
|
||||
|
||||
assert %{
|
||||
data: %{
|
||||
"postLibrary" => [%{"a" => "1a2", "b" => "1b2"}, %{"a" => "1a2", "b" => "1b2"}]
|
||||
}
|
||||
} = result
|
||||
end
|
||||
|
||||
test "a read with a non-id primary key fills in the id field" do
|
||||
record =
|
||||
AshGraphql.Test.NonIdPrimaryKey
|
||||
|
|
Loading…
Reference in a new issue