mirror of
https://github.com/ash-project/ash_graphql.git
synced 2024-09-19 12:53:40 +12:00
improvement: support metadata on read actions
metadata on read actions is merged with the rest of the fields on the query, so must have a name unique from the attributes/calculations/aggregates. The system will warn you if there is metadata on the underlying action that is being ignored, and will tell you how to fix it.
This commit is contained in:
parent
839bdcb126
commit
f0836f18b6
8 changed files with 280 additions and 64 deletions
|
@ -95,7 +95,14 @@ defmodule AshGraphql.Graphql.Resolver do
|
|||
|
||||
{result, _} ->
|
||||
resolution
|
||||
|> Absinthe.Resolution.put_result(to_resolution(result, context, api))
|
||||
|> Absinthe.Resolution.put_result(
|
||||
to_resolution(
|
||||
result
|
||||
|> add_read_metadata(gql_query, Ash.Resource.Info.action(query.resource, action)),
|
||||
context,
|
||||
api
|
||||
)
|
||||
)
|
||||
|> add_root_errors(api, result)
|
||||
|> modify_resolution(modify, modify_args)
|
||||
end
|
||||
|
@ -113,7 +120,8 @@ defmodule AshGraphql.Graphql.Resolver do
|
|||
def resolve(
|
||||
%{arguments: args, context: context} = resolution,
|
||||
{api, resource,
|
||||
%{name: query_name, type: :read_one, action: action, modify_resolution: modify}}
|
||||
%{name: query_name, type: :read_one, action: action, modify_resolution: modify} =
|
||||
gql_query}
|
||||
) do
|
||||
metadata = %{
|
||||
api: api,
|
||||
|
@ -171,6 +179,9 @@ defmodule AshGraphql.Graphql.Resolver do
|
|||
{{:error, error}, [query, {:error, error}]}
|
||||
end
|
||||
|
||||
result =
|
||||
add_read_metadata(result, gql_query, Ash.Resource.Info.action(query.resource, action))
|
||||
|
||||
resolution
|
||||
|> Absinthe.Resolution.put_result(to_resolution(result, context, api))
|
||||
|> add_root_errors(api, result)
|
||||
|
@ -195,7 +206,7 @@ defmodule AshGraphql.Graphql.Resolver do
|
|||
relay?: relay?,
|
||||
action: action,
|
||||
modify_resolution: modify
|
||||
}}
|
||||
} = gql_query}
|
||||
) do
|
||||
metadata = %{
|
||||
api: api,
|
||||
|
@ -247,6 +258,9 @@ defmodule AshGraphql.Graphql.Resolver do
|
|||
{{:error, error}, [query, {:error, error}]}
|
||||
end
|
||||
|
||||
result =
|
||||
add_read_metadata(result, gql_query, Ash.Resource.Info.action(query.resource, action))
|
||||
|
||||
resolution
|
||||
|> Absinthe.Resolution.put_result(to_resolution(result, context, api))
|
||||
|> add_root_errors(api, modify_args)
|
||||
|
@ -1187,6 +1201,32 @@ defmodule AshGraphql.Graphql.Resolver do
|
|||
end
|
||||
end
|
||||
|
||||
defp add_read_metadata({:ok, result}, query, action) do
|
||||
{:ok, add_read_metadata(result, query, action)}
|
||||
end
|
||||
|
||||
defp add_read_metadata(nil, _, _), do: nil
|
||||
|
||||
defp add_read_metadata(result, query, action) when is_list(result) do
|
||||
show_metadata = query.show_metadata || Enum.map(Map.get(action, :metadata, []), & &1.name)
|
||||
|
||||
Enum.map(result, fn record ->
|
||||
do_add_read_metadata(record, show_metadata)
|
||||
end)
|
||||
end
|
||||
|
||||
defp add_read_metadata(result, query, action) do
|
||||
show_metadata = query.show_metadata || Enum.map(Map.get(action, :metadata, []), & &1.name)
|
||||
|
||||
do_add_read_metadata(result, show_metadata)
|
||||
end
|
||||
|
||||
defp do_add_read_metadata(record, show_metadata) do
|
||||
Enum.reduce(show_metadata, record, fn key, record ->
|
||||
Map.put(record, key, Map.get(record.__metadata__ || %{}, key))
|
||||
end)
|
||||
end
|
||||
|
||||
defp add_metadata(result, action_result, action) do
|
||||
metadata = Map.get(action, :metadata, [])
|
||||
|
||||
|
|
|
@ -8,10 +8,14 @@ defmodule AshGraphql.Resource.Query do
|
|||
:allow_nil?,
|
||||
:modify_resolution,
|
||||
as_mutation?: false,
|
||||
metadata_names: [],
|
||||
metadata_types: [],
|
||||
show_metadata: nil,
|
||||
type_name: nil,
|
||||
relay?: false
|
||||
]
|
||||
|
||||
@get_schema [
|
||||
@query_schema [
|
||||
name: [
|
||||
type: :atom,
|
||||
doc: "The name to use for the query.",
|
||||
|
@ -22,27 +26,30 @@ defmodule AshGraphql.Resource.Query do
|
|||
doc: "The action to use for the query.",
|
||||
required: true
|
||||
],
|
||||
identity: [
|
||||
type_name: [
|
||||
type: :atom,
|
||||
doc: "The identity to use for looking up the record. Pass `false` to not use an identity.",
|
||||
required: false
|
||||
],
|
||||
allow_nil?: [
|
||||
type: :boolean,
|
||||
default: true,
|
||||
doc: "Whether or not the action can return nil."
|
||||
],
|
||||
modify_resolution: [
|
||||
type: :mfa,
|
||||
doc: """
|
||||
An MFA that will be called with the resolution, the query, and the result of the action as the first three arguments (followed by the arguments in the mfa).
|
||||
Must return a new absinthe resolution. This can be used to implement things like setting cookies based on resource actions. A method of using resolution context
|
||||
for that is documented here: https://hexdocs.pm/absinthe_plug/Absinthe.Plug.html#module-before-send
|
||||
Override the type name returned by this query. Must be set if the read action has `metadata`.
|
||||
|
||||
*Important* if you are modifying the context, then you should also set `as_mutation?` to true and represent
|
||||
this in your graphql as a mutation. See `as_mutation?` for more.
|
||||
To ignore any action metadata, set this to the same type the resource uses, or set `show_metadata` to `[]`.
|
||||
To show metadata in the response, choose a new name here, like `:user_with_token` to get a response type that
|
||||
includes the additional fields.
|
||||
"""
|
||||
],
|
||||
metadata_names: [
|
||||
type: :keyword_list,
|
||||
default: [],
|
||||
doc: "Name overrides for metadata fields on the read action."
|
||||
],
|
||||
metadata_types: [
|
||||
type: :keyword_list,
|
||||
default: [],
|
||||
doc: "Type overrides for metadata fields on the read action."
|
||||
],
|
||||
show_metadata: [
|
||||
type: {:list, :atom},
|
||||
doc: "The metadata attributes to show. Defaults to all."
|
||||
],
|
||||
as_mutation?: [
|
||||
type: :boolean,
|
||||
default: false,
|
||||
|
@ -58,48 +65,56 @@ defmodule AshGraphql.Resource.Query do
|
|||
]
|
||||
]
|
||||
|
||||
@get_schema [
|
||||
identity: [
|
||||
type: :atom,
|
||||
doc:
|
||||
"The identity to use for looking up the record. Pass `false` to not use an identity.",
|
||||
required: false
|
||||
],
|
||||
allow_nil?: [
|
||||
type: :boolean,
|
||||
default: true,
|
||||
doc: "Whether or not the action can return nil."
|
||||
],
|
||||
modify_resolution: [
|
||||
type: :mfa,
|
||||
doc: """
|
||||
An MFA that will be called with the resolution, the query, and the result of the action as the first three arguments (followed by the arguments in the mfa).
|
||||
Must return a new absinthe resolution. This can be used to implement things like setting cookies based on resource actions. A method of using resolution context
|
||||
for that is documented here: https://hexdocs.pm/absinthe_plug/Absinthe.Plug.html#module-before-send
|
||||
|
||||
*Important* if you are modifying the context, then you should also set `as_mutation?` to true and represent
|
||||
this in your graphql as a mutation. See `as_mutation?` for more.
|
||||
"""
|
||||
]
|
||||
]
|
||||
|> Spark.OptionsHelpers.merge_schemas(@query_schema, "Shared Query Options")
|
||||
|
||||
@read_one_schema [
|
||||
name: [
|
||||
type: :atom,
|
||||
doc: "The name to use for the query.",
|
||||
default: :read_one
|
||||
],
|
||||
action: [
|
||||
type: :atom,
|
||||
doc: "The action to use for the query.",
|
||||
required: true
|
||||
],
|
||||
allow_nil?: [
|
||||
type: :boolean,
|
||||
default: true,
|
||||
doc: "Whether or not the action can return nil."
|
||||
]
|
||||
]
|
||||
allow_nil?: [
|
||||
type: :boolean,
|
||||
default: true,
|
||||
doc: "Whether or not the action can return nil."
|
||||
]
|
||||
]
|
||||
|> Spark.OptionsHelpers.merge_schemas(@query_schema, "Shared Query Options")
|
||||
|
||||
@list_schema [
|
||||
name: [
|
||||
type: :atom,
|
||||
doc: "The name to use for the query.",
|
||||
default: :list
|
||||
],
|
||||
action: [
|
||||
type: :atom,
|
||||
doc: "The action to use for the query.",
|
||||
required: true
|
||||
],
|
||||
relay?: [
|
||||
type: :boolean,
|
||||
default: false,
|
||||
doc: """
|
||||
If true, the graphql queries/resolvers for this resource will be built to honor the [relay specification](https://relay.dev/graphql/connections.htm).
|
||||
relay?: [
|
||||
type: :boolean,
|
||||
default: false,
|
||||
doc: """
|
||||
If true, the graphql queries/resolvers for this resource will be built to honor the [relay specification](https://relay.dev/graphql/connections.htm).
|
||||
|
||||
The two changes that are made currently are:
|
||||
The two changes that are made currently are:
|
||||
|
||||
* the type for the resource will implement the `Node` interface
|
||||
* pagination over that resource will behave as a Connection.
|
||||
"""
|
||||
]
|
||||
]
|
||||
* the type for the resource will implement the `Node` interface
|
||||
* pagination over that resource will behave as a Connection.
|
||||
"""
|
||||
]
|
||||
]
|
||||
|> Spark.OptionsHelpers.merge_schemas(@query_schema, "Shared Query Options")
|
||||
|
||||
def get_schema, do: @get_schema
|
||||
def read_one_schema, do: @read_one_schema
|
||||
|
|
|
@ -294,6 +294,10 @@ defmodule AshGraphql.Resource do
|
|||
AshGraphql.Resource.Transformers.ValidateCompatibleNames
|
||||
]
|
||||
|
||||
@verifiers [
|
||||
AshGraphql.Resource.Verifiers.VerifyQueryMetadata
|
||||
]
|
||||
|
||||
@sections [@graphql]
|
||||
|
||||
@moduledoc """
|
||||
|
@ -313,7 +317,7 @@ defmodule AshGraphql.Resource do
|
|||
<!--- ash-hq-hide-stop --> <!--- -->
|
||||
"""
|
||||
|
||||
use Spark.Dsl.Extension, sections: @sections, transformers: @transformers
|
||||
use Spark.Dsl.Extension, sections: @sections, transformers: @transformers, verifiers: @verifiers
|
||||
|
||||
@deprecated "See `AshGraphql.Resource.Info.queries/1`"
|
||||
defdelegate queries(resource), to: AshGraphql.Resource.Info
|
||||
|
@ -866,7 +870,9 @@ defmodule AshGraphql.Resource do
|
|||
end
|
||||
|
||||
# sobelow_skip ["DOS.StringToAtom"]
|
||||
defp query_type(%{type: :list, relay?: relay?}, _resource, action, type) do
|
||||
defp query_type(%{type: :list, relay?: relay?} = query, _resource, action, type) do
|
||||
type = query.type_name || type
|
||||
|
||||
if action.pagination do
|
||||
cond do
|
||||
relay? ->
|
||||
|
@ -890,6 +896,8 @@ defmodule AshGraphql.Resource do
|
|||
end
|
||||
|
||||
defp query_type(query, _resource, _action, type) do
|
||||
type = query.type_name || type
|
||||
|
||||
maybe_wrap_non_null(type, not query.allow_nil?)
|
||||
end
|
||||
|
||||
|
@ -1197,6 +1205,7 @@ defmodule AshGraphql.Resource do
|
|||
def type_definitions(resource, api, schema) do
|
||||
List.wrap(calculation_input(resource, schema)) ++
|
||||
List.wrap(type_definition(resource, api, schema)) ++
|
||||
List.wrap(query_type_definitions(resource, api, schema)) ++
|
||||
List.wrap(sort_input(resource, schema)) ++
|
||||
List.wrap(filter_input(resource, schema)) ++
|
||||
filter_field_types(resource, schema) ++
|
||||
|
@ -2474,6 +2483,42 @@ defmodule AshGraphql.Resource do
|
|||
type.identifier == :node
|
||||
end
|
||||
|
||||
def query_type_definitions(resource, api, schema) do
|
||||
resource_type = AshGraphql.Resource.Info.type(resource)
|
||||
|
||||
resource
|
||||
|> AshGraphql.Resource.Info.queries()
|
||||
|> Enum.filter(&(&1.type_name && &1.type_name != resource_type))
|
||||
|> Enum.map(fn query ->
|
||||
relay? = Map.get(query, :relay?)
|
||||
|
||||
interfaces =
|
||||
if relay? do
|
||||
[:node]
|
||||
else
|
||||
[]
|
||||
end
|
||||
|
||||
is_type_of =
|
||||
if relay? do
|
||||
&AshGraphql.Resource.is_node_type/1
|
||||
else
|
||||
nil
|
||||
end
|
||||
|
||||
%Absinthe.Blueprint.Schema.ObjectTypeDefinition{
|
||||
description: Ash.Resource.Info.description(resource),
|
||||
interfaces: interfaces,
|
||||
fields: fields(resource, api, schema, query),
|
||||
identifier: query.type_name,
|
||||
module: schema,
|
||||
name: Macro.camelize(to_string(query.type_name)),
|
||||
__reference__: ref(__ENV__),
|
||||
is_type_of: is_type_of
|
||||
}
|
||||
end)
|
||||
end
|
||||
|
||||
def type_definition(resource, api, schema) do
|
||||
if generate_object?(resource) do
|
||||
type = AshGraphql.Resource.Info.type(resource)
|
||||
|
@ -2510,14 +2555,48 @@ defmodule AshGraphql.Resource do
|
|||
end
|
||||
end
|
||||
|
||||
defp fields(resource, api, schema) do
|
||||
defp fields(resource, api, schema, query \\ nil) do
|
||||
attributes(resource, schema) ++
|
||||
metadata(query, resource, schema) ++
|
||||
relationships(resource, api, schema) ++
|
||||
aggregates(resource, schema) ++
|
||||
calculations(resource, api, schema) ++
|
||||
keyset(resource, schema)
|
||||
end
|
||||
|
||||
defp metadata(nil, _resource, _schema) do
|
||||
[]
|
||||
end
|
||||
|
||||
defp metadata(query, resource, schema) do
|
||||
action = Ash.Resource.Info.action(resource, query.action)
|
||||
show_metadata = query.show_metadata || Enum.map(Map.get(action, :metadata, []), & &1.name)
|
||||
|
||||
action.metadata
|
||||
|> Enum.filter(&(&1.name in show_metadata))
|
||||
|> Enum.map(fn metadata ->
|
||||
field_type =
|
||||
case query.metadata_types[metadata.name] do
|
||||
nil ->
|
||||
metadata.type
|
||||
|> field_type(metadata, resource)
|
||||
|> maybe_wrap_non_null(not metadata.allow_nil?)
|
||||
|
||||
type ->
|
||||
unwrap_literal_type(type)
|
||||
end
|
||||
|
||||
%Absinthe.Blueprint.Schema.FieldDefinition{
|
||||
description: metadata.description,
|
||||
identifier: metadata.name,
|
||||
module: schema,
|
||||
name: to_string(query.metadata_names[metadata.name] || metadata.name),
|
||||
type: field_type,
|
||||
__reference__: ref(__ENV__)
|
||||
}
|
||||
end)
|
||||
end
|
||||
|
||||
defp keyset(resource, schema) do
|
||||
case AshGraphql.Resource.Info.keyset_field(resource) do
|
||||
nil ->
|
||||
|
|
41
lib/resource/verifiers/verify_query_metadata.ex
Normal file
41
lib/resource/verifiers/verify_query_metadata.ex
Normal file
|
@ -0,0 +1,41 @@
|
|||
defmodule AshGraphql.Resource.Verifiers.VerifyQueryMetadata do
|
||||
@moduledoc "Ensures that queries for actions with metadata have a type set"
|
||||
use Spark.Dsl.Verifier
|
||||
|
||||
alias Spark.Dsl.Transformer
|
||||
|
||||
def verify(dsl) do
|
||||
dsl
|
||||
|> AshGraphql.Resource.Info.queries()
|
||||
|> Enum.each(fn query ->
|
||||
action = Ash.Resource.Info.action(dsl, query.action)
|
||||
show_metadata = query.show_metadata || Enum.map(Map.get(action, :metadata, []), & &1.name)
|
||||
|
||||
metadata =
|
||||
action
|
||||
|> Map.get(:metadata, [])
|
||||
|> Enum.filter(&(&1.name in show_metadata))
|
||||
|
||||
if action && !Enum.empty?(metadata) && is_nil(query.type_name) do
|
||||
resource = Transformer.get_persisted(dsl, :module)
|
||||
|
||||
raise Spark.Error.DslError,
|
||||
module: resource,
|
||||
message: """
|
||||
Queries for actions with metadata must have a type configured on the query.
|
||||
|
||||
The #{query.action} action on #{inspect(resource)} has the following metadata fields:
|
||||
|
||||
#{Enum.map_join(action.metadata, "\n", &"* #{&1.name}")}
|
||||
|
||||
To generate a new type and include the metadata in that type, provide a new type
|
||||
name, for example `type :user_with_token`.
|
||||
|
||||
To ignore the generated metadata, use the same type as the default.
|
||||
"""
|
||||
end
|
||||
end)
|
||||
|
||||
:ok
|
||||
end
|
||||
end
|
2
mix.exs
2
mix.exs
|
@ -134,7 +134,7 @@ defmodule AshGraphql.MixProject do
|
|||
# Run "mix help deps" to learn about dependencies.
|
||||
defp deps do
|
||||
[
|
||||
{:ash, ash_version("~> 2.4")},
|
||||
{:ash, ash_version("~> 2.5.11")},
|
||||
{:absinthe_plug, "~> 1.4"},
|
||||
{:absinthe, "~> 1.7"},
|
||||
{:dataloader, "~> 1.0"},
|
||||
|
|
4
mix.lock
4
mix.lock
|
@ -1,7 +1,7 @@
|
|||
%{
|
||||
"absinthe": {:hex, :absinthe, "1.7.0", "36819e7b1fd5046c9c734f27fe7e564aed3bda59f0354c37cd2df88fd32dd014", [:mix], [{:dataloader, "~> 1.0.0", [hex: :dataloader, repo: "hexpm", optional: true]}, {:decimal, "~> 1.0 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: true]}, {:nimble_parsec, "~> 0.5 or ~> 1.0", [hex: :nimble_parsec, repo: "hexpm", optional: false]}, {:telemetry, "~> 1.0 or ~> 0.4", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "566a5b5519afc9b29c4d367f0c6768162de3ec03e9bf9916f9dc2bcbe7c09643"},
|
||||
"absinthe_plug": {:hex, :absinthe_plug, "1.5.5", "be913e77df1947ffb654a1cf1a90e28d84dc23241f6404053750bae513ccd52b", [:mix], [{:absinthe, "~> 1.5", [hex: :absinthe, repo: "hexpm", optional: false]}, {:plug, "~> 1.4", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm", "6c366615d9422444774206aff3448bb9cfb4e849e0c9a94a275085097bc67509"},
|
||||
"ash": {:hex, :ash, "2.5.4", "fc93280ee500ec1d06df9c18a47dba963c90cacc9f3b43a6de2d04489f5408a1", [:mix], [{:comparable, "~> 1.0", [hex: :comparable, repo: "hexpm", optional: false]}, {:decimal, "~> 2.0", [hex: :decimal, repo: "hexpm", optional: false]}, {:earmark, "~> 1.4", [hex: :earmark, repo: "hexpm", optional: true]}, {:ecto, "~> 3.7", [hex: :ecto, repo: "hexpm", optional: false]}, {:ets, "~> 0.8.0", [hex: :ets, repo: "hexpm", optional: false]}, {:jason, ">= 1.0.0", [hex: :jason, repo: "hexpm", optional: false]}, {:picosat_elixir, "~> 0.2", [hex: :picosat_elixir, repo: "hexpm", optional: false]}, {:plug, ">= 0.0.0", [hex: :plug, repo: "hexpm", optional: true]}, {:spark, ">= 0.3.0", [hex: :spark, repo: "hexpm", optional: false]}, {:stream_data, "~> 0.5.0", [hex: :stream_data, repo: "hexpm", optional: false]}, {:telemetry, "~> 1.1", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "a0196dc73d1b9dcd4fc4e59ba1359e42cfa0252b06fc4a23a043f82c5b5f3d19"},
|
||||
"ash": {:hex, :ash, "2.5.11", "eeadeba560681d53be5438a15434c121f0dc8b108cdf05e975a40fe809865df4", [:mix], [{:comparable, "~> 1.0", [hex: :comparable, repo: "hexpm", optional: false]}, {:decimal, "~> 2.0", [hex: :decimal, repo: "hexpm", optional: false]}, {:earmark, "~> 1.4", [hex: :earmark, repo: "hexpm", optional: true]}, {:ecto, "~> 3.7", [hex: :ecto, repo: "hexpm", optional: false]}, {:ets, "~> 0.8.0", [hex: :ets, repo: "hexpm", optional: false]}, {:jason, ">= 1.0.0", [hex: :jason, repo: "hexpm", optional: false]}, {:picosat_elixir, "~> 0.2", [hex: :picosat_elixir, repo: "hexpm", optional: false]}, {:plug, ">= 0.0.0", [hex: :plug, repo: "hexpm", optional: true]}, {:spark, ">= 0.3.0", [hex: :spark, repo: "hexpm", optional: false]}, {:stream_data, "~> 0.5.0", [hex: :stream_data, repo: "hexpm", optional: false]}, {:telemetry, "~> 1.1", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "bf2b5e337650b7bdc805af2a6038f5126a344c0439320606f5f8db9e11962071"},
|
||||
"bunt": {:hex, :bunt, "0.2.0", "951c6e801e8b1d2cbe58ebbd3e616a869061ddadcc4863d0a2182541acae9a38", [:mix], [], "hexpm", "7af5c7e09fe1d40f76c8e4f9dd2be7cebd83909f31fee7cd0e9eadc567da8353"},
|
||||
"certifi": {:hex, :certifi, "2.9.0", "6f2a475689dd47f19fb74334859d460a2dc4e3252a3324bd2111b8f0429e7e21", [:rebar3], [], "hexpm", "266da46bdb06d6c6d35fde799bcb28d36d985d424ad7c08b5bb48f5b5cdd4641"},
|
||||
"comparable": {:hex, :comparable, "1.0.0", "bb669e91cedd14ae9937053e5bcbc3c52bb2f22422611f43b6e38367d94a495f", [:mix], [{:typable, "~> 0.1", [hex: :typable, repo: "hexpm", optional: false]}], "hexpm", "277c11eeb1cd726e7cd41c6c199e7e52fa16ee6830b45ad4cdc62e51f62eb60c"},
|
||||
|
@ -36,7 +36,7 @@
|
|||
"plug_crypto": {:hex, :plug_crypto, "1.2.3", "8f77d13aeb32bfd9e654cb68f0af517b371fb34c56c9f2b58fe3df1235c1251a", [:mix], [], "hexpm", "b5672099c6ad5c202c45f5a403f21a3411247f164e4a8fab056e5cd8a290f4a2"},
|
||||
"sobelow": {:hex, :sobelow, "0.11.0", "cdc17e3a9f1ea78dc55dbe0a03121cb6767fef737c6d9f1e62ee7e78730abccc", [:mix], [{:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "c57807bfe6f231338b657781f89ef0320b66a0dbe779aa911d6ed27cfa14ae6e"},
|
||||
"sourceror": {:hex, :sourceror, "0.11.2", "549ce48be666421ac60cfb7f59c8752e0d393baa0b14d06271d3f6a8c1b027ab", [:mix], [], "hexpm", "9ab659118896a36be6eec68ff7b0674cba372fc8e210b1e9dc8cf2b55bb70dfb"},
|
||||
"spark": {:hex, :spark, "0.3.5", "99905e681156050a713218e2b57956870b88b660dff57e7ee061b0245fc5dd50", [:mix], [{:nimble_options, "~> 0.5", [hex: :nimble_options, repo: "hexpm", optional: false]}, {:sourceror, "~> 0.1", [hex: :sourceror, repo: "hexpm", optional: false]}], "hexpm", "f272fe581f37123bf90568974937dbc18ce575fc43580a5a658965b35b993520"},
|
||||
"spark": {:hex, :spark, "0.3.8", "002b2855be1bc37c4c31be49cb409f08626606c71c97615e6ce57c3ab3da654a", [:mix], [{:nimble_options, "~> 0.5", [hex: :nimble_options, repo: "hexpm", optional: false]}, {:sourceror, "~> 0.1", [hex: :sourceror, repo: "hexpm", optional: false]}], "hexpm", "78e7491bf18ef2c2ac43684c69f0e8c4b4447ac5af3ad66e6448a9d9e0f5c411"},
|
||||
"ssl_verify_fun": {:hex, :ssl_verify_fun, "1.1.6", "cf344f5692c82d2cd7554f5ec8fd961548d4fd09e7d22f5b62482e5aeaebd4b0", [:make, :mix, :rebar3], [], "hexpm", "bdb0d2471f453c88ff3908e7686f86f9be327d065cc1ec16fa4540197ea04680"},
|
||||
"stream_data": {:hex, :stream_data, "0.5.0", "b27641e58941685c75b353577dc602c9d2c12292dd84babf506c2033cd97893e", [:mix], [], "hexpm", "012bd2eec069ada4db3411f9115ccafa38540a3c78c4c0349f151fc761b9e271"},
|
||||
"telemetry": {:hex, :telemetry, "1.2.1", "68fdfe8d8f05a8428483a97d7aab2f268aaff24b49e0f599faa091f1d4e7f61c", [:rebar3], [], "hexpm", "dad9ce9d8effc621708f99eac538ef1cbe05d6a874dd741de2e689c47feafed5"},
|
||||
|
|
|
@ -38,6 +38,29 @@ defmodule AshGraphql.ReadTest do
|
|||
assert %{data: %{"postScore" => [%{"text" => "foo"}]}} = result
|
||||
end
|
||||
|
||||
test "metadata fields are rendered" do
|
||||
AshGraphql.Test.User
|
||||
|> Ash.Changeset.for_create(:create,
|
||||
name: "My Name"
|
||||
)
|
||||
|> AshGraphql.Test.Api.create!()
|
||||
|
||||
resp =
|
||||
"""
|
||||
query CurrentUserWithMetadata {
|
||||
currentUserWithMetadata {
|
||||
bar
|
||||
}
|
||||
}
|
||||
"""
|
||||
|> Absinthe.run(AshGraphql.Test.Schema)
|
||||
|
||||
assert {:ok, result} = resp
|
||||
|
||||
refute Map.has_key?(result, :errors)
|
||||
assert %{data: %{"currentUserWithMetadata" => %{"bar" => "bar"}}} = result
|
||||
end
|
||||
|
||||
test "a read with arguments works" do
|
||||
AshGraphql.Test.Post
|
||||
|> Ash.Changeset.for_create(:create, text: "foo", published: true)
|
||||
|
|
|
@ -10,7 +10,12 @@ defmodule AshGraphql.Test.User do
|
|||
type :user
|
||||
|
||||
queries do
|
||||
read_one :current_user, :current_user
|
||||
read_one(:current_user, :current_user)
|
||||
|
||||
read_one :current_user_with_metadata, :current_user_with_metadata do
|
||||
type_name :user_with_bar
|
||||
metadata_names(foo: :bar)
|
||||
end
|
||||
end
|
||||
|
||||
mutations do
|
||||
|
@ -27,6 +32,19 @@ defmodule AshGraphql.Test.User do
|
|||
read :current_user do
|
||||
filter(id: actor(:id))
|
||||
end
|
||||
|
||||
read :current_user_with_metadata do
|
||||
metadata(:foo, :string)
|
||||
|
||||
prepare(fn query, _ ->
|
||||
Ash.Query.after_action(query, fn _query, results ->
|
||||
{:ok,
|
||||
Enum.map(results, fn result ->
|
||||
Ash.Resource.put_metadata(result, :foo, "bar")
|
||||
end)}
|
||||
end)
|
||||
end)
|
||||
end
|
||||
end
|
||||
|
||||
attributes do
|
||||
|
|
Loading…
Reference in a new issue