mirror of
https://github.com/ash-project/ash_graphql.git
synced 2024-09-19 12:53:40 +12:00
WIP
This commit is contained in:
parent
7563cbf971
commit
759c92553c
6 changed files with 195 additions and 125 deletions
|
@ -133,17 +133,111 @@ defmodule AshGraphql.Api.Schema do
|
|||
|
||||
defp query_fields(api, resource) do
|
||||
resource
|
||||
|> Ash.actions()
|
||||
|> Enum.flat_map(fn action ->
|
||||
case action do
|
||||
%{type: :read, primary?: true} ->
|
||||
read_action(api, resource, action)
|
||||
|> AshGraphql.fields()
|
||||
|> Enum.map(fn field ->
|
||||
get_one_identifier = AshGraphql.type(resource)
|
||||
|
||||
_ ->
|
||||
# TODO: Only support reads
|
||||
[]
|
||||
case field.type do
|
||||
:get ->
|
||||
%Absinthe.Type.Field{
|
||||
__private__: [],
|
||||
__reference__: %{
|
||||
location: %{file: "nofile", line: 1},
|
||||
module: AshExample.Api.Schema
|
||||
},
|
||||
args: %{
|
||||
id: %Absinthe.Type.Argument{
|
||||
__reference__: nil,
|
||||
default_value: nil,
|
||||
definition: nil,
|
||||
deprecation: nil,
|
||||
description: nil,
|
||||
identifier: :id,
|
||||
name: "id",
|
||||
type: %Absinthe.Type.NonNull{of_type: :id}
|
||||
}
|
||||
},
|
||||
# TODO: DO THIS
|
||||
complexity: 2,
|
||||
config: %{},
|
||||
default_value: nil,
|
||||
definition: AshExample.Api.Schema,
|
||||
deprecation: nil,
|
||||
description: nil,
|
||||
identifier: field.name,
|
||||
middleware: [
|
||||
{{AshGraphql.Graphql.Resolver, :resolve}, {api, resource, :get, field.action}}
|
||||
],
|
||||
name: Atom.to_string(field.name),
|
||||
triggers: [],
|
||||
type: get_one_identifier
|
||||
}
|
||||
|
||||
:read ->
|
||||
%Absinthe.Type.Field{
|
||||
__private__: [],
|
||||
__reference__: %{
|
||||
location: %{file: "nofile", line: 1},
|
||||
module: AshExample.Api.Schema
|
||||
},
|
||||
args: %{
|
||||
limit: %Absinthe.Type.Argument{
|
||||
identifier: :limit,
|
||||
type: :integer,
|
||||
name: "limit"
|
||||
},
|
||||
offset: %Absinthe.Type.Argument{
|
||||
identifier: :offset,
|
||||
default_value: 0,
|
||||
type: :integer,
|
||||
name: "offset"
|
||||
}
|
||||
# TODO: Generate types for the filter, sort, and paginate args
|
||||
# Also figure out graphql pagination
|
||||
# filter: %Absinthe.Type.Argument
|
||||
# id: %Absinthe.Type.Argument{
|
||||
# __reference__: nil,
|
||||
# default_value: nil,
|
||||
# definition: nil,
|
||||
# deprecation: nil,
|
||||
# description: nil,
|
||||
# identifier: :id,
|
||||
# name: "id",
|
||||
# type: %Absinthe.Type.NonNull{of_type: :id}
|
||||
# }
|
||||
},
|
||||
complexity: 1,
|
||||
config: %{},
|
||||
default_value: nil,
|
||||
definition: AshExample.Api.Schema,
|
||||
deprecation: nil,
|
||||
description: nil,
|
||||
identifier: field.name,
|
||||
middleware: [
|
||||
{{AshGraphql.Graphql.Resolver, :resolve}, {api, resource, :read, field.action}}
|
||||
],
|
||||
name: Atom.to_string(field.name),
|
||||
triggers: [],
|
||||
type: String.to_atom("page_of_#{get_one_identifier}")
|
||||
}
|
||||
end
|
||||
end)
|
||||
|
||||
# for field <- AshGraphql.fields(resource) do
|
||||
|
||||
# end
|
||||
# resource
|
||||
# |> Ash.actions()
|
||||
# |> Enum.flat_map(fn action ->
|
||||
# case action do
|
||||
# %{type: :read, primary?: true} ->
|
||||
# read_action(api, resource, action)
|
||||
|
||||
# _ ->
|
||||
# # TODO: Only support reads
|
||||
# []
|
||||
# end
|
||||
# end)
|
||||
end
|
||||
|
||||
# defp resource_types(api, resource) do
|
||||
|
@ -164,8 +258,7 @@ defmodule AshGraphql.Api.Schema do
|
|||
# raise "Invalid primary key #{primary_key} for graphql resource"
|
||||
# end
|
||||
|
||||
get_one_identifier = String.to_atom(Ash.type(resource))
|
||||
get_many_identifier = String.to_atom(Ash.name(resource))
|
||||
get_one_identifier = AshGraphql.type(resource)
|
||||
|
||||
[
|
||||
%Absinthe.Type.Object{
|
||||
|
@ -187,7 +280,7 @@ defmodule AshGraphql.Api.Schema do
|
|||
is_type_of: :object,
|
||||
name: String.capitalize(Atom.to_string(get_one_identifier))
|
||||
},
|
||||
page_of(get_one_identifier, get_many_identifier)
|
||||
page_of(get_one_identifier)
|
||||
]
|
||||
end
|
||||
|
||||
|
@ -220,95 +313,7 @@ defmodule AshGraphql.Api.Schema do
|
|||
# }
|
||||
end
|
||||
|
||||
defp read_action(api, resource, action) do
|
||||
# If not primary action, we need to give it a different name
|
||||
get_one_identifier = String.to_atom(Ash.type(resource))
|
||||
get_many_identifier = String.to_atom(Ash.name(resource))
|
||||
|
||||
[
|
||||
%Absinthe.Type.Field{
|
||||
__private__: [],
|
||||
__reference__: %{
|
||||
location: %{file: "nofile", line: 1},
|
||||
module: AshExample.Api.Schema
|
||||
},
|
||||
args: %{
|
||||
id: %Absinthe.Type.Argument{
|
||||
__reference__: nil,
|
||||
default_value: nil,
|
||||
definition: nil,
|
||||
deprecation: nil,
|
||||
description: nil,
|
||||
identifier: :id,
|
||||
name: "id",
|
||||
type: %Absinthe.Type.NonNull{of_type: :id}
|
||||
}
|
||||
},
|
||||
# TODO: DO THIS
|
||||
complexity: 2,
|
||||
config: %{},
|
||||
default_value: nil,
|
||||
definition: AshExample.Api.Schema,
|
||||
deprecation: nil,
|
||||
description: nil,
|
||||
identifier: get_one_identifier,
|
||||
middleware: [
|
||||
{{AshGraphql.Graphql.Resolver, :resolve}, {api, resource, :get, action.name}}
|
||||
],
|
||||
name: Atom.to_string(get_one_identifier),
|
||||
triggers: [],
|
||||
type: get_one_identifier
|
||||
},
|
||||
%Absinthe.Type.Field{
|
||||
__private__: [],
|
||||
__reference__: %{
|
||||
location: %{file: "nofile", line: 1},
|
||||
module: AshExample.Api.Schema
|
||||
},
|
||||
args: %{
|
||||
limit: %Absinthe.Type.Argument{
|
||||
identifier: :limit,
|
||||
type: :integer,
|
||||
name: "limit"
|
||||
},
|
||||
offset: %Absinthe.Type.Argument{
|
||||
identifier: :offset,
|
||||
default_value: 0,
|
||||
type: :integer,
|
||||
name: "offset"
|
||||
}
|
||||
# TODO: Generate types for the filter, sort, and paginate args
|
||||
# Also figure out graphql pagination
|
||||
# filter: %Absinthe.Type.Argument
|
||||
# id: %Absinthe.Type.Argument{
|
||||
# __reference__: nil,
|
||||
# default_value: nil,
|
||||
# definition: nil,
|
||||
# deprecation: nil,
|
||||
# description: nil,
|
||||
# identifier: :id,
|
||||
# name: "id",
|
||||
# type: %Absinthe.Type.NonNull{of_type: :id}
|
||||
# }
|
||||
},
|
||||
complexity: 1,
|
||||
config: %{},
|
||||
default_value: nil,
|
||||
definition: AshExample.Api.Schema,
|
||||
deprecation: nil,
|
||||
description: nil,
|
||||
identifier: get_many_identifier,
|
||||
middleware: [
|
||||
{{AshGraphql.Graphql.Resolver, :resolve}, {api, resource, :read, action.name}}
|
||||
],
|
||||
name: Atom.to_string(get_many_identifier),
|
||||
triggers: [],
|
||||
type: String.to_atom("page_of_#{get_many_identifier}")
|
||||
}
|
||||
]
|
||||
end
|
||||
|
||||
defp page_of(get_one_identifier, get_many_identifier) do
|
||||
defp page_of(get_one_identifier) do
|
||||
%Absinthe.Type.Object{
|
||||
__private__: [__absinthe_referenced__: true],
|
||||
__reference__: %{
|
||||
|
@ -316,7 +321,7 @@ defmodule AshGraphql.Api.Schema do
|
|||
module: AshExample.Api.Schema
|
||||
},
|
||||
definition: AshExample.Api.Schema,
|
||||
description: "A page of #{get_many_identifier}",
|
||||
description: "A page of #{get_one_identifier}",
|
||||
fields: %{
|
||||
__typename: type_name_type(),
|
||||
offset: %Absinthe.Type.Field{
|
||||
|
@ -384,10 +389,10 @@ defmodule AshGraphql.Api.Schema do
|
|||
}
|
||||
}
|
||||
},
|
||||
identifier: String.to_atom("page_of_#{get_many_identifier}"),
|
||||
identifier: String.to_atom("page_of_#{get_one_identifier}"),
|
||||
interfaces: [],
|
||||
is_type_of: :object,
|
||||
name: "pageOf#{String.capitalize(Atom.to_string(get_many_identifier))}"
|
||||
name: "pageOf#{String.capitalize(Atom.to_string(get_one_identifier))}"
|
||||
}
|
||||
end
|
||||
|
||||
|
|
|
@ -3,16 +3,11 @@ defmodule AshGraphql do
|
|||
Documentation for `AshGraphql`.
|
||||
"""
|
||||
|
||||
@doc """
|
||||
Hello world.
|
||||
def fields(resource) do
|
||||
resource.graphql_fields()
|
||||
end
|
||||
|
||||
## Examples
|
||||
|
||||
iex> AshGraphql.hello()
|
||||
:world
|
||||
|
||||
"""
|
||||
def hello do
|
||||
:world
|
||||
def type(resource) do
|
||||
resource.graphql_type()
|
||||
end
|
||||
end
|
||||
|
|
|
@ -3,12 +3,14 @@ defmodule AshGraphql.Graphql.Resolver do
|
|||
%{arguments: %{id: id}, context: context} = resolution,
|
||||
{api, resource, :get, action}
|
||||
) do
|
||||
result =
|
||||
api.get(resource, id,
|
||||
action: action,
|
||||
authorize?: api.graphql_authorize?,
|
||||
actor: Map.get(context, :actor)
|
||||
)
|
||||
opts =
|
||||
if api.graphql_authorize?() do
|
||||
[actor: Map.get(context, :actor), action: action]
|
||||
else
|
||||
[action: action]
|
||||
end
|
||||
|
||||
result = api.get(resource, id, opts)
|
||||
|
||||
Absinthe.Resolution.put_result(resolution, to_resolution(result))
|
||||
end
|
||||
|
@ -17,16 +19,19 @@ defmodule AshGraphql.Graphql.Resolver do
|
|||
%{arguments: %{limit: limit, offset: offset}, context: context} = resolution,
|
||||
{api, resource, :read, action}
|
||||
) do
|
||||
opts =
|
||||
if api.graphql_authorize?() do
|
||||
[actor: Map.get(context, :actor), action: action]
|
||||
else
|
||||
[action: action]
|
||||
end
|
||||
|
||||
result =
|
||||
resource
|
||||
|> api.query
|
||||
|> Ash.Query.limit(limit)
|
||||
|> Ash.Query.offset(offset)
|
||||
|> api.read(
|
||||
action: action,
|
||||
actor: Map.get(context, :actor),
|
||||
authorize?: api.graphql_authorize?()
|
||||
)
|
||||
|> api.read(opts)
|
||||
|> case do
|
||||
{:ok, results} ->
|
||||
{:ok, %AshGraphql.Paginator{results: results, limit: limit, offset: offset}}
|
||||
|
|
|
@ -1,14 +1,63 @@
|
|||
defmodule AshGraphql.GraphqlResource do
|
||||
@callback graphql_fields() :: [%AshGraphql.GraphqlResource.Field{}]
|
||||
@callback graphql_type() :: atom
|
||||
|
||||
defmacro __using__(_) do
|
||||
quote do
|
||||
@extensions AshGraphql.GraphqlResource
|
||||
@behaviour AshGraphql.GraphqlResource
|
||||
@graphql_type nil
|
||||
Module.register_attribute(__MODULE__, :graphql_fields, accumulate: true)
|
||||
|
||||
import AshGraphql.GraphqlResource, only: [graphql: 1]
|
||||
end
|
||||
end
|
||||
|
||||
defmacro graphql(do: body) do
|
||||
quote do
|
||||
import AshGraphql.GraphqlResource, only: [fields: 1, type: 1]
|
||||
unquote(body)
|
||||
import AshGraphql.GraphqlResource, only: [graphql: 1]
|
||||
end
|
||||
end
|
||||
|
||||
defmacro fields(do: body) do
|
||||
quote do
|
||||
import AshGraphql.GraphqlResource, only: [field: 2, field: 3]
|
||||
|
||||
unquote(body)
|
||||
|
||||
import AshGraphql.GraphqlResource, only: [fields: 1, type: 1]
|
||||
end
|
||||
end
|
||||
|
||||
defmacro type(type) do
|
||||
quote do
|
||||
@graphql_type unquote(type)
|
||||
end
|
||||
end
|
||||
|
||||
defmacro field(name, action, opts \\ []) do
|
||||
quote do
|
||||
field = AshGraphql.GraphqlResource.Field.new(unquote(name), unquote(action), unquote(opts))
|
||||
@graphql_fields field
|
||||
end
|
||||
end
|
||||
|
||||
@doc false
|
||||
def before_compile_hook(_env) do
|
||||
quote do
|
||||
:ok
|
||||
unless @graphql_type do
|
||||
raise "Must set graphql type for #{__MODULE__}"
|
||||
end
|
||||
|
||||
def graphql_type() do
|
||||
@graphql_type
|
||||
end
|
||||
|
||||
def graphql_fields() do
|
||||
@graphql_fields
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
15
lib/graphql_resource/field.ex
Normal file
15
lib/graphql_resource/field.ex
Normal file
|
@ -0,0 +1,15 @@
|
|||
defmodule AshGraphql.GraphqlResource.Field do
|
||||
defstruct [:name, :action, :type]
|
||||
|
||||
def new(name, action, opts) do
|
||||
if opts[:type] && opts[:type] not in [:read, :get] do
|
||||
raise "Can only specify `read` or `get` for `type`"
|
||||
end
|
||||
|
||||
%__MODULE__{
|
||||
name: name,
|
||||
action: action,
|
||||
type: opts[:type] || :read
|
||||
}
|
||||
end
|
||||
end
|
1
mix.lock
1
mix.lock
|
@ -6,6 +6,7 @@
|
|||
"elixir_make": {:hex, :elixir_make, "0.6.0", "38349f3e29aff4864352084fc736fa7fa0f2995a819a737554f7ebd28b85aaab", [:mix], [], "hexpm", "d522695b93b7f0b4c0fcb2dfe73a6b905b1c301226a5a55cb42e5b14d509e050"},
|
||||
"ets": {:hex, :ets, "0.8.0", "90153faafd289bb0801a537d5b05661f46d5e70b2bb55cccf5ab7f0d41d07832", [:mix], [], "hexpm", "bda4e05b16eada36798cfda16db551dc5243c0adc9a6dfe655b1bc1279b99cb8"},
|
||||
"machinery": {:hex, :machinery, "1.0.0", "df6968d84c651b9971a33871c78c10157b6e13e4f3390b0bee5b0e8bdea8c781", [:mix], [{:ecto, "~> 3.0", [hex: :ecto, repo: "hexpm", optional: false]}], "hexpm", "4f6eb4185a48e7245360bedf653af4acc6fa6ae8ff4690619395543fa1a8395f"},
|
||||
"nimble_options": {:hex, :nimble_options, "0.2.1", "7eac99688c2544d4cc3ace36ee8f2bf4d738c14d031bd1e1193aab096309d488", [:mix], [], "hexpm", "ca48293609306791ce2634818d849b7defe09330adb7e4e1118a0bc59bed1cf4"},
|
||||
"nimble_parsec": {:hex, :nimble_parsec, "0.5.3", "def21c10a9ed70ce22754fdeea0810dafd53c2db3219a0cd54cf5526377af1c6", [:mix], [], "hexpm", "589b5af56f4afca65217a1f3eb3fee7e79b09c40c742fddc1c312b3ac0b3399f"},
|
||||
"picosat_elixir": {:hex, :picosat_elixir, "0.1.3", "1e4eab27786b7dc7764c307555d8943cbba82912ed943737372760377be05ec8", [:make, :mix], [{:elixir_make, "~> 0.4", [hex: :elixir_make, repo: "hexpm", optional: false]}], "hexpm", "3add4d5ea1afa49f51bb7576bae000fe88091969804cc25baf47ffec48a9c626"},
|
||||
"telemetry": {:hex, :telemetry, "0.4.1", "ae2718484892448a24470e6aa341bc847c3277bfb8d4e9289f7474d752c09c7f", [:rebar3], [], "hexpm", "4738382e36a0a9a2b6e25d67c960e40e1a2c95560b9f936d8e29de8cd858480f"},
|
||||
|
|
Loading…
Reference in a new issue