diff --git a/lib/resource/resource.ex b/lib/resource/resource.ex index 8a0ff52..67577e7 100644 --- a/lib/resource/resource.ex +++ b/lib/resource/resource.ex @@ -634,7 +634,7 @@ defmodule AshGraphql.Resource do |> Enum.filter(& &1.writable?) |> Enum.map(fn attribute -> allow_nil? = - attribute.allow_nil? || attribute.default || type == :update || + attribute.allow_nil? || attribute.default || type == :update || attribute.generated? || (type == :create && attribute.name in action.allow_nil_input) explicitly_required = attribute.name in action.require_attributes @@ -642,7 +642,7 @@ defmodule AshGraphql.Resource do field_type = attribute.type |> field_type(attribute, resource, true) - |> maybe_wrap_non_null(explicitly_required || attribute_required?(attribute)) + |> maybe_wrap_non_null(explicitly_required || not allow_nil?) %Absinthe.Blueprint.Schema.FieldDefinition{ description: attribute.description, @@ -663,7 +663,7 @@ defmodule AshGraphql.Resource do type = argument.type |> field_type(argument, resource, true) - |> maybe_wrap_non_null(attribute_required?(argument)) + |> maybe_wrap_non_null(argument_required?(argument)) %Absinthe.Blueprint.Schema.FieldDefinition{ identifier: argument.name, @@ -689,7 +689,7 @@ defmodule AshGraphql.Resource do identifier: argument.name, module: schema, name: to_string(argument.name), - type: maybe_wrap_non_null(type, attribute_required?(argument)), + type: maybe_wrap_non_null(type, argument_required?(argument)), __reference__: ref(__ENV__) } end @@ -883,7 +883,7 @@ defmodule AshGraphql.Resource do type = argument.type |> field_type(argument, resource, true) - |> maybe_wrap_non_null(attribute_required?(argument)) + |> maybe_wrap_non_null(argument_required?(argument)) %Absinthe.Blueprint.Schema.FieldDefinition{ identifier: argument.name, @@ -1986,10 +1986,9 @@ defmodule AshGraphql.Resource do end end - defp attribute_required?(%{allow_nil?: true}), do: false - defp attribute_required?(%{generated?: true}), do: false - defp attribute_required?(%{default: default}) when not is_nil(default), do: false - defp attribute_required?(_), do: true + defp argument_required?(%{allow_nil?: true}), do: false + defp argument_required?(%{default: default}) when not is_nil(default), do: false + defp argument_required?(_), do: true # sobelow_skip ["DOS.StringToAtom"] defp relationships(resource, api, schema) do diff --git a/test/create_test.exs b/test/create_test.exs index 38656d3..23f46de 100644 --- a/test/create_test.exs +++ b/test/create_test.exs @@ -58,6 +58,56 @@ defmodule AshGraphql.CreateTest do } = result end + test "a create with a managed relationship works with many_to_many and [on_lookup: :relate, on_match: :relate]" do + resp = + """ + mutation CreatePostWithCommentsAndTags($input: CreatePostWithCommentsAndTagsInput) { + createPostWithCommentsAndTags(input: $input) { + result{ + text + comments(sort:{field:TEXT}){ + text + } + tags(sort:{field:NAME}){ + name + } + } + errors{ + message + } + } + } + """ + |> Absinthe.run(AshGraphql.Test.Schema, + variables: %{ + "input" => %{ + "text" => "foobar", + "comments" => [ + %{"text" => "foobar"}, + %{"text" => "barfoo"} + ], + "tags" => [%{"name" => "test"}, %{"name" => "tag"}] + } + } + ) + + assert {:ok, result} = resp + + refute Map.has_key?(result, :errors) + + assert %{ + data: %{ + "createPostWithCommentsAndTags" => %{ + "result" => %{ + "text" => "foobar", + "comments" => [%{"text" => "barfoo"}, %{"text" => "foobar"}], + "tags" => [%{"name" => "tag"}, %{"name" => "test"}] + } + } + } + } = result + end + test "a create with arguments works" do resp = """ diff --git a/test/support/api.ex b/test/support/api.ex index 2998078..af070ae 100644 --- a/test/support/api.ex +++ b/test/support/api.ex @@ -6,6 +6,8 @@ defmodule AshGraphql.Test.Api do resources do resource(AshGraphql.Test.Comment) resource(AshGraphql.Test.Post) + resource(AshGraphql.Test.PostTag) + resource(AshGraphql.Test.Tag) resource(AshGraphql.Test.User) end end diff --git a/test/support/resources/post.ex b/test/support/resources/post.ex index 02147a0..c8327b1 100644 --- a/test/support/resources/post.ex +++ b/test/support/resources/post.ex @@ -17,6 +17,13 @@ defmodule AshGraphql.Test.Post do managed_relationships do managed_relationship :with_comments, :comments + + managed_relationship :with_comments_and_tags, :comments, + type_name: :create_post_comment_with_tag + + managed_relationship :with_comments_and_tags, :tags, + lookup_with_primary_key?: false, + lookup_identities: [:name] end mutations do @@ -24,6 +31,7 @@ defmodule AshGraphql.Test.Post do create :upsert_post, :upsert, upsert?: true create :create_post_with_comments, :with_comments + create :create_post_with_comments_and_tags, :with_comments_and_tags update :update_post, :update update :update_best_post, :update, read_action: :best_post, identity: false @@ -55,6 +63,14 @@ defmodule AshGraphql.Test.Post do change(manage_relationship(:comments, type: :direct_control)) end + create :with_comments_and_tags do + argument(:comments, {:array, :map}) + argument(:tags, {:array, :map}, allow_nil?: false) + + change(manage_relationship(:comments, on_lookup: :relate, on_no_match: :create)) + change(manage_relationship(:tags, on_lookup: :relate, on_no_match: :create)) + end + read :paginated do pagination(required?: true, offset?: true, countable: true) end @@ -104,5 +120,11 @@ defmodule AshGraphql.Test.Post do relationships do has_many(:comments, AshGraphql.Test.Comment) has_many(:paginated_comments, AshGraphql.Test.Comment, read_action: :paginated) + + many_to_many(:tags, AshGraphql.Test.Tag, + through: AshGraphql.Test.PostTag, + source_field_on_join_table: :post_id, + destination_field_on_join_table: :tag_id + ) end end diff --git a/test/support/resources/post_tag.ex b/test/support/resources/post_tag.ex new file mode 100644 index 0000000..a3b206a --- /dev/null +++ b/test/support/resources/post_tag.ex @@ -0,0 +1,18 @@ +defmodule AshGraphql.Test.PostTag do + @moduledoc false + + use Ash.Resource, + data_layer: Ash.DataLayer.Ets + + relationships do + belongs_to :post, AshGraphql.Test.Post do + primary_key?(true) + required?(true) + end + + belongs_to :tag, AshGraphql.Test.Tag do + primary_key?(true) + required?(true) + end + end +end diff --git a/test/support/resources/tag.ex b/test/support/resources/tag.ex new file mode 100644 index 0000000..dd0c749 --- /dev/null +++ b/test/support/resources/tag.ex @@ -0,0 +1,45 @@ +defmodule AshGraphql.Test.Tag do + @moduledoc false + + use Ash.Resource, + data_layer: Ash.DataLayer.Ets, + extensions: [AshGraphql.Resource] + + graphql do + type(:tag) + + queries do + get :get_tag, :read + list :get_tags, :read + end + + mutations do + create :create_tag, :create + destroy :destroy_tag, :destroy + end + end + + actions do + create :create do + primary?(true) + end + end + + attributes do + uuid_primary_key(:id) + + attribute(:name, :string) + end + + identities do + identity(:name, [:name]) + end + + relationships do + many_to_many(:posts, AshGraphql.Test.Post, + through: AshGraphql.Test.PostTag, + source_field_on_join_table: :tag_id, + destination_field_on_join_table: :post_id + ) + end +end