mirror of
https://github.com/ash-project/ash_graphql.git
synced 2024-09-20 21:32:59 +12:00
297 lines
9.4 KiB
Elixir
297 lines
9.4 KiB
Elixir
defmodule AshGraphql.Api do
|
|
@graphql %Spark.Dsl.Section{
|
|
name: :graphql,
|
|
describe: """
|
|
Global configuration for graphql
|
|
""",
|
|
examples: [
|
|
"""
|
|
graphql do
|
|
authorize? false # To skip authorization for this API
|
|
end
|
|
"""
|
|
],
|
|
schema: [
|
|
authorize?: [
|
|
type: :boolean,
|
|
doc: "Whether or not to perform authorization for this API",
|
|
default: true
|
|
],
|
|
tracer: [
|
|
type: :atom,
|
|
doc:
|
|
"A tracer to use to trace execution in the graphql. Will use `config :ash, :tracer` if it is set."
|
|
],
|
|
root_level_errors?: [
|
|
type: :boolean,
|
|
default: false,
|
|
doc:
|
|
"By default, mutation errors are shown in their result object's errors key, but this setting places those errors in the top level errors list"
|
|
],
|
|
error_handler: [
|
|
type: :mfa,
|
|
default: {AshGraphql.DefaultErrorHandler, :handle_error, []},
|
|
doc: """
|
|
Set an MFA to intercept/handle any errors that are generated.
|
|
"""
|
|
],
|
|
show_raised_errors?: [
|
|
type: :boolean,
|
|
default: false,
|
|
doc:
|
|
"For security purposes, if an error is *raised* then Ash simply shows a generic error. If you want to show those errors, set this to true."
|
|
],
|
|
debug?: [
|
|
type: :boolean,
|
|
doc: "Whether or not to log (extremely verbose) debug information",
|
|
default: false
|
|
]
|
|
]
|
|
}
|
|
|
|
@sections [@graphql]
|
|
|
|
@moduledoc """
|
|
The entrypoint for adding graphql behavior to an Ash API
|
|
|
|
<!--- ash-hq-hide-start --> <!--- -->
|
|
|
|
## DSL Documentation
|
|
|
|
### Index
|
|
|
|
#{Spark.Dsl.Extension.doc_index(@sections)}
|
|
|
|
### Docs
|
|
|
|
#{Spark.Dsl.Extension.doc(@sections)}
|
|
<!--- ash-hq-hide-stop --> <!--- -->
|
|
"""
|
|
|
|
require Ash.Api.Info
|
|
|
|
use Spark.Dsl.Extension, sections: @sections
|
|
|
|
@deprecated "See `AshGraphql.Api.Info.authorize?/1`"
|
|
defdelegate authorize?(api), to: AshGraphql.Api.Info
|
|
|
|
@deprecated "See `AshGraphql.Api.Info.root_level_errors?/1`"
|
|
defdelegate root_level_errors?(api), to: AshGraphql.Api.Info
|
|
|
|
@deprecated "See `AshGraphql.Api.Info.show_raised_errors?/1`"
|
|
defdelegate show_raised_errors?(api), to: AshGraphql.Api.Info
|
|
|
|
@deprecated "See `AshGraphql.Api.Info.debug?/1`"
|
|
defdelegate debug?(api), to: AshGraphql.Api.Info
|
|
|
|
@doc false
|
|
def queries(api, resources, action_middleware, schema) do
|
|
Enum.flat_map(resources, &AshGraphql.Resource.queries(api, &1, action_middleware, schema))
|
|
end
|
|
|
|
@doc false
|
|
def mutations(api, resources, action_middleware, schema) do
|
|
resources
|
|
|> Enum.filter(fn resource ->
|
|
AshGraphql.Resource in Spark.extensions(resource)
|
|
end)
|
|
|> Enum.flat_map(&AshGraphql.Resource.mutations(api, &1, action_middleware, schema))
|
|
end
|
|
|
|
@doc false
|
|
def type_definitions(api, resources, schema, env, first?) do
|
|
resource_types =
|
|
resources
|
|
|> Enum.reject(&Ash.Resource.Info.embedded?/1)
|
|
|> Enum.flat_map(fn resource ->
|
|
if AshGraphql.Resource in Spark.extensions(resource) &&
|
|
AshGraphql.Resource.Info.type(resource) do
|
|
AshGraphql.Resource.type_definitions(resource, api, schema) ++
|
|
AshGraphql.Resource.mutation_types(resource, schema)
|
|
else
|
|
AshGraphql.Resource.no_graphql_types(resource, schema)
|
|
end
|
|
end)
|
|
|
|
if first? do
|
|
[
|
|
%Absinthe.Blueprint.Schema.InterfaceTypeDefinition{
|
|
description: "A relay node",
|
|
name: "Node",
|
|
fields: [
|
|
%Absinthe.Blueprint.Schema.FieldDefinition{
|
|
description: "A unique identifier",
|
|
identifier: :id,
|
|
module: schema,
|
|
name: "id",
|
|
__reference__: AshGraphql.Resource.ref(env),
|
|
type: %Absinthe.Blueprint.TypeReference.NonNull{of_type: :id}
|
|
}
|
|
],
|
|
identifier: :node,
|
|
__reference__: AshGraphql.Resource.ref(env),
|
|
module: schema
|
|
},
|
|
%Absinthe.Blueprint.Schema.ObjectTypeDefinition{
|
|
description: "A relay page info",
|
|
name: "PageInfo",
|
|
fields: [
|
|
%Absinthe.Blueprint.Schema.FieldDefinition{
|
|
description: "When paginating backwards, are there more items?",
|
|
identifier: :has_previous_page,
|
|
module: schema,
|
|
name: "has_previous_page",
|
|
__reference__: AshGraphql.Resource.ref(env),
|
|
type: %Absinthe.Blueprint.TypeReference.NonNull{of_type: :boolean}
|
|
},
|
|
%Absinthe.Blueprint.Schema.FieldDefinition{
|
|
description: "When paginating forwards, are there more items?",
|
|
identifier: :has_next_page,
|
|
module: schema,
|
|
name: "has_next_page",
|
|
__reference__: AshGraphql.Resource.ref(env),
|
|
type: %Absinthe.Blueprint.TypeReference.NonNull{of_type: :boolean}
|
|
},
|
|
%Absinthe.Blueprint.Schema.FieldDefinition{
|
|
description: "When paginating backwards, the cursor to continue",
|
|
identifier: :start_cursor,
|
|
module: schema,
|
|
name: "start_cursor",
|
|
__reference__: AshGraphql.Resource.ref(env),
|
|
type: :string
|
|
},
|
|
%Absinthe.Blueprint.Schema.FieldDefinition{
|
|
description: "When paginating forwards, the cursor to continue",
|
|
identifier: :end_cursor,
|
|
module: schema,
|
|
name: "end_cursor",
|
|
__reference__: AshGraphql.Resource.ref(env),
|
|
type: :string
|
|
}
|
|
# 'count' field is not compatible with keyset pagination
|
|
],
|
|
identifier: :page_info,
|
|
module: schema,
|
|
__reference__: AshGraphql.Resource.ref(env)
|
|
}
|
|
] ++ resource_types
|
|
else
|
|
resource_types
|
|
end
|
|
end
|
|
|
|
def global_type_definitions(schema, env) do
|
|
[mutation_error(schema, env), sort_order(schema, env)]
|
|
end
|
|
|
|
defp sort_order(schema, env) do
|
|
%Absinthe.Blueprint.Schema.EnumTypeDefinition{
|
|
module: schema,
|
|
name: "SortOrder",
|
|
values: [
|
|
%Absinthe.Blueprint.Schema.EnumValueDefinition{
|
|
module: schema,
|
|
identifier: :desc,
|
|
__reference__: AshGraphql.Resource.ref(env),
|
|
name: "DESC",
|
|
value: :desc
|
|
},
|
|
%Absinthe.Blueprint.Schema.EnumValueDefinition{
|
|
module: schema,
|
|
identifier: :desc_nils_first,
|
|
__reference__: AshGraphql.Resource.ref(env),
|
|
name: "DESC_NULLS_FIRST",
|
|
value: :des_nils_first
|
|
},
|
|
%Absinthe.Blueprint.Schema.EnumValueDefinition{
|
|
module: schema,
|
|
identifier: :desc_nils_last,
|
|
__reference__: AshGraphql.Resource.ref(env),
|
|
name: "DESC_NULLS_LAST",
|
|
value: :desc_nils_last
|
|
},
|
|
%Absinthe.Blueprint.Schema.EnumValueDefinition{
|
|
module: schema,
|
|
identifier: :asc,
|
|
__reference__: AshGraphql.Resource.ref(env),
|
|
name: "ASC",
|
|
value: :asc
|
|
},
|
|
%Absinthe.Blueprint.Schema.EnumValueDefinition{
|
|
module: schema,
|
|
identifier: :asc_nils_first,
|
|
__reference__: AshGraphql.Resource.ref(env),
|
|
name: "ASC_NULLS_FIRST",
|
|
value: :asc_nils_first
|
|
},
|
|
%Absinthe.Blueprint.Schema.EnumValueDefinition{
|
|
module: schema,
|
|
identifier: :asc_nils_last,
|
|
__reference__: AshGraphql.Resource.ref(env),
|
|
name: "ASC_NULLS_LAST",
|
|
value: :asc_nils_last
|
|
}
|
|
],
|
|
identifier: :sort_order,
|
|
__reference__: AshGraphql.Resource.ref(env)
|
|
}
|
|
end
|
|
|
|
defp mutation_error(schema, env) do
|
|
%Absinthe.Blueprint.Schema.ObjectTypeDefinition{
|
|
description: "An error generated by a failed mutation",
|
|
fields: error_fields(schema, env),
|
|
identifier: :mutation_error,
|
|
module: schema,
|
|
name: "MutationError",
|
|
__reference__: AshGraphql.Resource.ref(env)
|
|
}
|
|
end
|
|
|
|
defp error_fields(schema, env) do
|
|
[
|
|
%Absinthe.Blueprint.Schema.FieldDefinition{
|
|
description: "The human readable error message",
|
|
identifier: :message,
|
|
module: schema,
|
|
__reference__: AshGraphql.Resource.ref(env),
|
|
name: "message",
|
|
type: :string
|
|
},
|
|
%Absinthe.Blueprint.Schema.FieldDefinition{
|
|
description: "A shorter error message, with vars not replaced",
|
|
identifier: :short_message,
|
|
module: schema,
|
|
__reference__: AshGraphql.Resource.ref(env),
|
|
name: "short_message",
|
|
type: :string
|
|
},
|
|
%Absinthe.Blueprint.Schema.FieldDefinition{
|
|
description: "Replacements for the short message",
|
|
identifier: :vars,
|
|
module: schema,
|
|
__reference__: AshGraphql.Resource.ref(env),
|
|
name: "vars",
|
|
type: :json
|
|
},
|
|
%Absinthe.Blueprint.Schema.FieldDefinition{
|
|
description: "An error code for the given error",
|
|
identifier: :code,
|
|
module: schema,
|
|
__reference__: AshGraphql.Resource.ref(env),
|
|
name: "code",
|
|
type: :string
|
|
},
|
|
%Absinthe.Blueprint.Schema.FieldDefinition{
|
|
description: "The field or fields that produced the error",
|
|
identifier: :fields,
|
|
module: schema,
|
|
__reference__: AshGraphql.Resource.ref(env),
|
|
name: "fields",
|
|
type: %Absinthe.Blueprint.TypeReference.List{
|
|
of_type: :string
|
|
}
|
|
}
|
|
]
|
|
end
|
|
end
|