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
1 change: 1 addition & 0 deletions .formatter.exs
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ spark_locals_without_parens = [
relationships: 1,
relay?: 1,
relay_id_translations: 1,
result_name: 1,
root_level_errors?: 1,
show_fields: 1,
show_metadata: 1,
Expand Down
4 changes: 4 additions & 0 deletions documentation/dsls/DSL-AshGraphql.Domain.md
Original file line number Diff line number Diff line change
Expand Up @@ -337,6 +337,7 @@ create :create_post, :create
| [`modify_resolution`](#graphql-mutations-create-modify_resolution){: #graphql-mutations-create-modify_resolution } | `mfa` | | An MFA that will be called with the resolution, the query, and the result of the action as the first three arguments. See the [the guide](/documentation/topics/modifying-the-resolution.html) for more. |
| [`meta`](#graphql-mutations-create-meta){: #graphql-mutations-create-meta } | `keyword` | `[]` | A keyword list of metadata for the mutation. |
| [`group`](#graphql-mutations-create-group){: #graphql-mutations-create-group } | `atom` | | An optional grouping key used to nest this mutation under a group wrapper. |
| [`result_name`](#graphql-mutations-create-result_name){: #graphql-mutations-create-result_name } | `atom` | | The GraphQL field name for the mutation result. Defaults to `result`. |



Expand Down Expand Up @@ -384,6 +385,7 @@ update :update_post, :update
| [`modify_resolution`](#graphql-mutations-update-modify_resolution){: #graphql-mutations-update-modify_resolution } | `mfa` | | An MFA that will be called with the resolution, the query, and the result of the action as the first three arguments. See the [the guide](/documentation/topics/modifying-the-resolution.html) for more. |
| [`meta`](#graphql-mutations-update-meta){: #graphql-mutations-update-meta } | `keyword` | `[]` | A keyword list of metadata for the mutation. |
| [`group`](#graphql-mutations-update-group){: #graphql-mutations-update-group } | `atom` | | An optional grouping key used to nest this mutation under a group wrapper. |
| [`result_name`](#graphql-mutations-update-result_name){: #graphql-mutations-update-result_name } | `atom` | | The GraphQL field name for the mutation result. Defaults to `result`. |



Expand Down Expand Up @@ -431,6 +433,7 @@ destroy :destroy_post, :destroy
| [`modify_resolution`](#graphql-mutations-destroy-modify_resolution){: #graphql-mutations-destroy-modify_resolution } | `mfa` | | An MFA that will be called with the resolution, the query, and the result of the action as the first three arguments. See the [the guide](/documentation/topics/modifying-the-resolution.html) for more. |
| [`meta`](#graphql-mutations-destroy-meta){: #graphql-mutations-destroy-meta } | `keyword` | `[]` | A keyword list of metadata for the mutation. |
| [`group`](#graphql-mutations-destroy-group){: #graphql-mutations-destroy-group } | `atom` | | An optional grouping key used to nest this mutation under a group wrapper. |
| [`result_name`](#graphql-mutations-destroy-result_name){: #graphql-mutations-destroy-result_name } | `atom` | | The GraphQL field name for the mutation result. Defaults to `result`. |



Expand Down Expand Up @@ -476,6 +479,7 @@ action :check_status, :check_status
| [`labels`](#graphql-mutations-action-labels){: #graphql-mutations-action-labels } | `atom \| list(atom)` | `[]` | Labels used to include or exclude this action from schemas with matching labels. |
| [`meta`](#graphql-mutations-action-meta){: #graphql-mutations-action-meta } | `keyword` | `[]` | A keyword list of metadata for the action. |
| [`args`](#graphql-mutations-action-args){: #graphql-mutations-action-args } | `list(atom)` | | A list of action attributes or arguments that should get their own arguments in the mutation instead of being passed in an input object. |
| [`result_name`](#graphql-mutations-action-result_name){: #graphql-mutations-action-result_name } | `atom` | | The GraphQL field name for the mutation result. Defaults to `result`. |



Expand Down
8 changes: 8 additions & 0 deletions documentation/dsls/DSL-AshGraphql.Resource.md
Original file line number Diff line number Diff line change
Expand Up @@ -613,6 +613,7 @@ create :create_post, :create
| [`modify_resolution`](#graphql-mutations-create-modify_resolution){: #graphql-mutations-create-modify_resolution } | `mfa` | | An MFA that will be called with the resolution, the query, and the result of the action as the first three arguments. See the [the guide](/documentation/topics/modifying-the-resolution.html) for more. |
| [`meta`](#graphql-mutations-create-meta){: #graphql-mutations-create-meta } | `keyword` | `[]` | A keyword list of metadata for the mutation. |
| [`group`](#graphql-mutations-create-group){: #graphql-mutations-create-group } | `atom` | | An optional grouping key used to nest this mutation under a group wrapper. |
| [`result_name`](#graphql-mutations-create-result_name){: #graphql-mutations-create-result_name } | `atom` | | The GraphQL field name for the mutation result. Defaults to `result`. |



Expand Down Expand Up @@ -659,6 +660,7 @@ update :update_post, :update
| [`modify_resolution`](#graphql-mutations-update-modify_resolution){: #graphql-mutations-update-modify_resolution } | `mfa` | | An MFA that will be called with the resolution, the query, and the result of the action as the first three arguments. See the [the guide](/documentation/topics/modifying-the-resolution.html) for more. |
| [`meta`](#graphql-mutations-update-meta){: #graphql-mutations-update-meta } | `keyword` | `[]` | A keyword list of metadata for the mutation. |
| [`group`](#graphql-mutations-update-group){: #graphql-mutations-update-group } | `atom` | | An optional grouping key used to nest this mutation under a group wrapper. |
| [`result_name`](#graphql-mutations-update-result_name){: #graphql-mutations-update-result_name } | `atom` | | The GraphQL field name for the mutation result. Defaults to `result`. |



Expand Down Expand Up @@ -705,6 +707,7 @@ destroy :destroy_post, :destroy
| [`modify_resolution`](#graphql-mutations-destroy-modify_resolution){: #graphql-mutations-destroy-modify_resolution } | `mfa` | | An MFA that will be called with the resolution, the query, and the result of the action as the first three arguments. See the [the guide](/documentation/topics/modifying-the-resolution.html) for more. |
| [`meta`](#graphql-mutations-destroy-meta){: #graphql-mutations-destroy-meta } | `keyword` | `[]` | A keyword list of metadata for the mutation. |
| [`group`](#graphql-mutations-destroy-group){: #graphql-mutations-destroy-group } | `atom` | | An optional grouping key used to nest this mutation under a group wrapper. |
| [`result_name`](#graphql-mutations-destroy-result_name){: #graphql-mutations-destroy-result_name } | `atom` | | The GraphQL field name for the mutation result. Defaults to `result`. |



Expand Down Expand Up @@ -749,6 +752,7 @@ action :check_status, :check_status
| [`labels`](#graphql-mutations-action-labels){: #graphql-mutations-action-labels } | `atom \| list(atom)` | `[]` | Labels used to include or exclude this action from schemas with matching labels. |
| [`meta`](#graphql-mutations-action-meta){: #graphql-mutations-action-meta } | `keyword` | `[]` | A keyword list of metadata for the action. |
| [`args`](#graphql-mutations-action-args){: #graphql-mutations-action-args } | `list(atom)` | | A list of action attributes or arguments that should get their own arguments in the mutation instead of being passed in an input object. |
| [`result_name`](#graphql-mutations-action-result_name){: #graphql-mutations-action-result_name } | `atom` | | The GraphQL field name for the mutation result. Defaults to `result`. |



Expand Down Expand Up @@ -821,6 +825,7 @@ create :create_post, :create
| [`modify_resolution`](#graphql-mutations-group-create-modify_resolution){: #graphql-mutations-group-create-modify_resolution } | `mfa` | | An MFA that will be called with the resolution, the query, and the result of the action as the first three arguments. See the [the guide](/documentation/topics/modifying-the-resolution.html) for more. |
| [`meta`](#graphql-mutations-group-create-meta){: #graphql-mutations-group-create-meta } | `keyword` | `[]` | A keyword list of metadata for the mutation. |
| [`group`](#graphql-mutations-group-create-group){: #graphql-mutations-group-create-group } | `atom` | | An optional grouping key used to nest this mutation under a group wrapper. |
| [`result_name`](#graphql-mutations-group-create-result_name){: #graphql-mutations-group-create-result_name } | `atom` | | The GraphQL field name for the mutation result. Defaults to `result`. |



Expand Down Expand Up @@ -867,6 +872,7 @@ update :update_post, :update
| [`modify_resolution`](#graphql-mutations-group-update-modify_resolution){: #graphql-mutations-group-update-modify_resolution } | `mfa` | | An MFA that will be called with the resolution, the query, and the result of the action as the first three arguments. See the [the guide](/documentation/topics/modifying-the-resolution.html) for more. |
| [`meta`](#graphql-mutations-group-update-meta){: #graphql-mutations-group-update-meta } | `keyword` | `[]` | A keyword list of metadata for the mutation. |
| [`group`](#graphql-mutations-group-update-group){: #graphql-mutations-group-update-group } | `atom` | | An optional grouping key used to nest this mutation under a group wrapper. |
| [`result_name`](#graphql-mutations-group-update-result_name){: #graphql-mutations-group-update-result_name } | `atom` | | The GraphQL field name for the mutation result. Defaults to `result`. |



Expand Down Expand Up @@ -913,6 +919,7 @@ destroy :destroy_post, :destroy
| [`modify_resolution`](#graphql-mutations-group-destroy-modify_resolution){: #graphql-mutations-group-destroy-modify_resolution } | `mfa` | | An MFA that will be called with the resolution, the query, and the result of the action as the first three arguments. See the [the guide](/documentation/topics/modifying-the-resolution.html) for more. |
| [`meta`](#graphql-mutations-group-destroy-meta){: #graphql-mutations-group-destroy-meta } | `keyword` | `[]` | A keyword list of metadata for the mutation. |
| [`group`](#graphql-mutations-group-destroy-group){: #graphql-mutations-group-destroy-group } | `atom` | | An optional grouping key used to nest this mutation under a group wrapper. |
| [`result_name`](#graphql-mutations-group-destroy-result_name){: #graphql-mutations-group-destroy-result_name } | `atom` | | The GraphQL field name for the mutation result. Defaults to `result`. |



Expand Down Expand Up @@ -957,6 +964,7 @@ action :check_status, :check_status
| [`labels`](#graphql-mutations-group-action-labels){: #graphql-mutations-group-action-labels } | `atom \| list(atom)` | `[]` | Labels used to include or exclude this action from schemas with matching labels. |
| [`meta`](#graphql-mutations-group-action-meta){: #graphql-mutations-group-action-meta } | `keyword` | `[]` | A keyword list of metadata for the action. |
| [`args`](#graphql-mutations-group-action-args){: #graphql-mutations-group-action-args } | `list(atom)` | | A list of action attributes or arguments that should get their own arguments in the mutation instead of being passed in an input object. |
| [`result_name`](#graphql-mutations-group-action-result_name){: #graphql-mutations-group-action-result_name } | `atom` | | The GraphQL field name for the mutation result. Defaults to `result`. |



Expand Down
57 changes: 44 additions & 13 deletions lib/graphql/resolver.ex
Original file line number Diff line number Diff line change
Expand Up @@ -1524,7 +1524,7 @@ defmodule AshGraphql.Graphql.Resolver do
upsert_identity: upsert_identity,
args: args,
modify_resolution: modify
}, _relay_ids?}
} = mutation, _relay_ids?}
) do
case handle_mutation_arguments(resource, action, nil, arguments, args) do
{:ok, input} ->
Expand Down Expand Up @@ -1561,6 +1561,7 @@ defmodule AshGraphql.Graphql.Resolver do
end

type_name = mutation_result_type(mutation_name)
result_field_name = mutation_result_field_name(mutation)

changeset =
resource
Expand All @@ -1571,7 +1572,7 @@ defmodule AshGraphql.Graphql.Resolver do
actor: Map.get(context, :actor),
authorize?: AshGraphql.Domain.Info.authorize?(domain)
)
|> select_fields(resource, resolution, type_name, ["result"])
|> select_fields(resource, resolution, type_name, [result_field_name])
|> load_fields(
[
domain: domain,
Expand All @@ -1585,7 +1586,7 @@ defmodule AshGraphql.Graphql.Resolver do
resolution.path,
context,
type_name,
["result"]
[result_field_name]
)

{result, modify_args} =
Expand Down Expand Up @@ -1654,7 +1655,7 @@ defmodule AshGraphql.Graphql.Resolver do
read_action: read_action,
args: args,
modify_resolution: modify
}, relay_ids?}
} = mutation, relay_ids?}
) do
read_action = read_action || Ash.Resource.Info.primary_action!(resource, :read).name

Expand Down Expand Up @@ -1689,6 +1690,8 @@ defmodule AshGraphql.Graphql.Resolver do
|> set_query_arguments(read_action, read_action_input)
|> Ash.Query.limit(1)

result_field_name = mutation_result_field_name(mutation)

{result, modify_args} =
query
|> Ash.bulk_update(action, input,
Expand All @@ -1706,7 +1709,7 @@ defmodule AshGraphql.Graphql.Resolver do
actor: Map.get(context, :actor),
select:
get_select(resource, resolution, mutation_result_type(mutation_name), [
"result"
result_field_name
]),
load:
get_loads(
Expand All @@ -1722,7 +1725,7 @@ defmodule AshGraphql.Graphql.Resolver do
resolution.path,
context,
mutation_result_type(mutation_name),
["result"]
[result_field_name]
)
)
|> case do
Expand Down Expand Up @@ -1824,7 +1827,7 @@ defmodule AshGraphql.Graphql.Resolver do
read_action: read_action,
args: args,
modify_resolution: modify
}, relay_ids?}
} = mutation, relay_ids?}
) do
read_action = read_action || Ash.Resource.Info.primary_action!(resource, :read).name

Expand All @@ -1851,14 +1854,23 @@ defmodule AshGraphql.Graphql.Resolver do

case filter do
{:ok, filter} ->
result_field_name = mutation_result_field_name(mutation)

query =
resource
|> Ash.Query.do_filter(filter)
|> Ash.Query.set_tenant(Map.get(context, :tenant))
|> Ash.Query.set_context(get_context(context))
|> set_query_arguments(read_action, read_action_input)
|> Ash.Query.limit(1)
|> pre_load_for_mutation(domain, resource, resolution, context, mutation_name)
|> pre_load_for_mutation(
domain,
resource,
resolution,
context,
mutation_name,
result_field_name
)

{result, modify_args} =
query
Expand All @@ -1877,7 +1889,7 @@ defmodule AshGraphql.Graphql.Resolver do
domain: domain,
select:
get_select(resource, resolution, mutation_result_type(mutation_name), [
"result"
result_field_name
])
)
|> case do
Expand Down Expand Up @@ -2128,7 +2140,15 @@ defmodule AshGraphql.Graphql.Resolver do

# Pre-load aggregates and calculations on the query before destruction
# to ensure they are available in the returned record for GraphQL serialization
defp pre_load_for_mutation(query, domain, resource, resolution, context, mutation_name) do
defp pre_load_for_mutation(
query,
domain,
resource,
resolution,
context,
mutation_name,
result_field_name
) do
load_opts = [
domain: domain,
tenant: Map.get(context, :tenant),
Expand All @@ -2145,7 +2165,7 @@ defmodule AshGraphql.Graphql.Resolver do
resolution.path,
context,
mutation_result_type(mutation_name),
["result"]
[result_field_name]
)
end

Expand Down Expand Up @@ -2219,7 +2239,7 @@ defmodule AshGraphql.Graphql.Resolver do
fields
end

selection = Enum.find(selections, &(&1.name == nested))
selection = Enum.find(selections, &field_matches_path?(&1, nested))

if selection do
nested_fields_and_path(resolution, [selection | path], rest)
Expand Down Expand Up @@ -2726,6 +2746,13 @@ defmodule AshGraphql.Graphql.Resolver do
String.to_atom("#{mutation_name}_result")
end

defp mutation_result_field_name(mutation) do
case Map.get(mutation, :result_name) do
nil -> "result"
name when is_atom(name) -> Atom.to_string(name)
end
end

# sobelow_skip ["DOS.StringToAtom"]
defp subscription_result_type(subscription_name) do
String.to_atom("#{subscription_name}_result")
Expand Down Expand Up @@ -2809,7 +2836,7 @@ defmodule AshGraphql.Graphql.Resolver do
cache = resolution.fields_cache

Enum.reduce(names, {project, cache}, fn name, {fields, cache} ->
case fields |> Enum.find(&(&1.name == name)) do
case fields |> Enum.find(&field_matches_path?(&1, name)) do
nil ->
{fields, cache}

Expand All @@ -2829,6 +2856,10 @@ defmodule AshGraphql.Graphql.Resolver do
|> elem(0)
end

defp field_matches_path?(field, name) do
field.name == name
end

defp names_only(fields) do
Enum.map(fields, & &1.schema_node.identifier)
end
Expand Down
5 changes: 5 additions & 0 deletions lib/resource/mutation.ex
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ defmodule AshGraphql.Resource.Mutation do
:modify_resolution,
:relay_id_translations,
:description,
:result_name,
:__spark_metadata__,
args: [],
hide_inputs: [],
Expand Down Expand Up @@ -76,6 +77,10 @@ defmodule AshGraphql.Resource.Mutation do
group: [
type: :atom,
doc: "An optional grouping key used to nest this mutation under a group wrapper."
],
result_name: [
type: :atom,
doc: "The GraphQL field name for the mutation result. Defaults to `result`."
]
]

Expand Down
16 changes: 13 additions & 3 deletions lib/resource/resource.ex
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,7 @@ defmodule AshGraphql.Resource do
:relay_id_translations,
:error_location,
:modify_resolution,
:result_name,
:__spark_metadata__,
args: [],
hide_inputs: [],
Expand Down Expand Up @@ -150,7 +151,7 @@ defmodule AshGraphql.Resource do
schema:
@action_schema
|> Spark.Options.merge(
Mutation.create_schema() |> Keyword.take([:args]),
Mutation.create_schema() |> Keyword.take([:args, :result_name]),
"Shared Mutation Options"
),
args: [:name, :action],
Expand Down Expand Up @@ -1543,14 +1544,16 @@ defmodule AshGraphql.Resource do
}
]

result_field_name = mutation_result_field_name(mutation)

fields =
if mutation.type == :action do
[
%Absinthe.Blueprint.Schema.FieldDefinition{
description: description,
identifier: :result,
module: schema,
name: "result",
name: result_field_name,
type:
generic_action_type(
%{mutation.action | allow_nil?: true},
Expand Down Expand Up @@ -1581,7 +1584,7 @@ defmodule AshGraphql.Resource do
description: description,
identifier: :result,
module: schema,
name: "result",
name: result_field_name,
type: resource_type,
__reference__: ref(__ENV__)
}
Expand Down Expand Up @@ -1648,6 +1651,13 @@ defmodule AshGraphql.Resource do
end)
end

defp mutation_result_field_name(mutation) do
case Map.get(mutation, :result_name) do
nil -> "result"
name when is_atom(name) -> Atom.to_string(name)
end
end

@doc false
# sobelow_skip ["DOS.StringToAtom"]
def query_types(resource, domain, all_domains, schema, labels \\ nil) do
Expand Down
Loading
Loading