Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 18 additions & 9 deletions lib/resource/resource.ex
Original file line number Diff line number Diff line change
Expand Up @@ -2745,13 +2745,22 @@ defmodule AshGraphql.Resource do
end

defp do_get_expressable_types(operator_types, field_type, array_type?) do
field_type_short_name =
case Ash.Type.short_names()
|> Enum.find(fn {_, type} -> type == field_type end) do
nil -> nil
{short_name, _} -> short_name
# For NewType subtypes, also match against the base type's short name
field_types =
case Ash.Type.NewType.new_type?(field_type) do
true ->
base_type = Ash.Type.NewType.subtype_of(field_type)
[base_type, field_type]

false ->
[field_type]
end

field_type_short_names =
Ash.Type.short_names()
|> Enum.filter(fn {_short_name, field_type} -> field_type in field_types end)
|> Enum.map(fn {short_name, _field_type} -> short_name end)

operator_types
|> Enum.filter(fn
[:any, {:array, type}] when is_atom(type) ->
Expand All @@ -2769,8 +2778,8 @@ defmodule AshGraphql.Resource do
[:any, type] when is_atom(type) ->
true

[^field_type_short_name, type] when is_atom(type) and not is_nil(field_type_short_name) ->
true
[expected_type, type] when is_atom(type) and is_atom(expected_type) ->
expected_type in field_type_short_names

_ ->
false
Expand Down Expand Up @@ -5027,8 +5036,8 @@ defmodule AshGraphql.Resource do
if Application.compile_env(:ash_graphql, :warn_on_json_fallback?, true) do
defp warn_on_json_fallback(resource, constraints) do
IO.warn("""
Struct type with instance_of constraint falls back to JsonString for input.
Consider creating a custom type with `use AshGraphql.Type` and `graphql_input_type/1`
Struct type with instance_of constraint falls back to JsonString for input.
Consider creating a custom type with `use AshGraphql.Type` and `graphql_input_type/1`
for structured validation.

Resource: #{inspect(resource)}
Expand Down
23 changes: 23 additions & 0 deletions test/filter_sort_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,29 @@ defmodule AshGraphql.FilterSortTest do
assert "greaterThan" in field_names
end

test "filterable_fields resolves NewType subtype_of for data layer function operator matching" do
resp =
"""
query {
__type(name: "TagWithIlikeFilterLabel") {
inputFields {
name
}
}
}

"""
|> Absinthe.run(AshGraphql.Test.Schema)

assert {:ok, %{data: %{"__type" => %{"inputFields" => input_fields}}}} = resp

field_names = Enum.map(input_fields, & &1["name"])
assert "eq" in field_names
assert "ilike" in field_names
refute "lessThan" in field_names
refute "in" in field_names
end

test "sortable_fields option is applied" do
resp =
"""
Expand Down
1 change: 1 addition & 0 deletions test/support/domain.ex
Original file line number Diff line number Diff line change
Expand Up @@ -72,5 +72,6 @@ defmodule AshGraphql.Test.Domain do
resource(AshGraphql.Test.ResourceWithTypedStruct)
resource(AshGraphql.Test.AfterTransactionEts)
resource(AshGraphql.Test.AfterTransactionMnesia)
resource(AshGraphql.Test.TagWithIlike)
end
end
105 changes: 105 additions & 0 deletions test/support/ets_with_functions.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
# SPDX-FileCopyrightText: 2020 ash_graphql contributors <https://github.com/ash-project/ash_graphql/graphs/contributors>
#
# SPDX-License-Identifier: MIT

defmodule AshGraphql.Test.DataLayer.EtsWithFunctions do
@moduledoc false
@behaviour Ash.DataLayer

@ets_with_functions %Spark.Dsl.Section{
name: :ets_with_functions,
describe: "ETS data layer with custom functions for testing",
schema: [
private?: [
type: :boolean,
default: false
],
table: [
type: :atom
],
repo: [
type: :atom,
doc: "A fake repo option to simulate AshPostgres pattern"
]
]
}

use Spark.Dsl.Extension,
sections: [@ets_with_functions]

@impl true
defdelegate can?(resource, feature), to: Ash.DataLayer.Ets

@impl true
defdelegate resource_to_query(resource, domain), to: Ash.DataLayer.Ets

@impl true
defdelegate limit(query, limit, resource), to: Ash.DataLayer.Ets

@impl true
defdelegate offset(query, offset, resource), to: Ash.DataLayer.Ets

@impl true
defdelegate add_calculations(query, calculations, resource), to: Ash.DataLayer.Ets

@impl true
defdelegate add_aggregate(query, aggregate, resource), to: Ash.DataLayer.Ets

@impl true
defdelegate set_tenant(resource, query, tenant), to: Ash.DataLayer.Ets

@impl true
defdelegate set_context(resource, query, context), to: Ash.DataLayer.Ets

@impl true
defdelegate select(query, select, resource), to: Ash.DataLayer.Ets

@impl true
defdelegate filter(query, filter, resource), to: Ash.DataLayer.Ets

@impl true
defdelegate sort(query, sort, resource), to: Ash.DataLayer.Ets

@impl true
defdelegate distinct(query, distinct, resource), to: Ash.DataLayer.Ets

@impl true
defdelegate distinct_sort(query, distinct_sort, resource), to: Ash.DataLayer.Ets

@impl true
defdelegate run_query(query, resource), to: Ash.DataLayer.Ets

@impl true
defdelegate run_aggregate_query(query, aggregates, resource), to: Ash.DataLayer.Ets

@impl true
defdelegate create(resource, changeset), to: Ash.DataLayer.Ets

@impl true
defdelegate destroy(resource, changeset), to: Ash.DataLayer.Ets

@impl true
defdelegate update(resource, changeset), to: Ash.DataLayer.Ets

@impl true
defdelegate combination_of(combinations, resource, domain), to: Ash.DataLayer.Ets

@impl true
defdelegate prefer_lateral_join_for_many_to_many?, to: Ash.DataLayer.Ets

@impl true
defdelegate calculate(resource, expressions, context), to: Ash.DataLayer.Ets

@impl true
def functions(resource) do
# Simulate AshPostgres pattern: access a DSL option, then call a method on it
# AshPostgres does: AshPostgres.DataLayer.Info.repo(resource, :mutate).config()
repo = Spark.Dsl.Extension.get_opt(resource, [:ets_with_functions], :repo, nil, true)

if repo do
_config = repo.config()
end

[AshGraphql.Test.Functions.TestILike]
end
end
34 changes: 34 additions & 0 deletions test/support/resources/tag_with_ilike.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
# SPDX-FileCopyrightText: 2020 ash_graphql contributors <https://github.com/ash-project/ash_graphql/graphs/contributors>
#
# SPDX-License-Identifier: MIT

defmodule AshGraphql.Test.TagWithIlike do
@moduledoc false

use Ash.Resource,
domain: AshGraphql.Test.Domain,
data_layer: AshGraphql.Test.DataLayer.EtsWithFunctions,
extensions: [AshGraphql.Resource]

graphql do
type(:tag_with_ilike)

filterable_fields [:name, label: [:eq, :ilike]]

queries do
list :get_tags_with_ilike, :read
end
end

actions do
default_accept(:*)
defaults([:read, :create])
end

attributes do
uuid_primary_key(:id)

attribute(:name, :string, public?: true)
attribute(:label, AshGraphql.Types.StringNewType, public?: true)
end
end
10 changes: 10 additions & 0 deletions test/support/test_ilike_function.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
# SPDX-FileCopyrightText: 2020 ash_graphql contributors <https://github.com/ash-project/ash_graphql/graphs/contributors>
#
# SPDX-License-Identifier: MIT

defmodule AshGraphql.Test.Functions.TestILike do
@moduledoc false
use Ash.Query.Function, name: :ilike, predicate?: true

def args, do: [[:string, :string]]
end
Loading