This commit is contained in:
Zach Daniel 2020-06-05 14:41:00 -04:00
parent 7563cbf971
commit 759c92553c
No known key found for this signature in database
GPG key ID: C377365383138D4B
6 changed files with 195 additions and 125 deletions

View file

@ -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

View file

@ -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

View file

@ -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}}

View file

@ -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

View 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

View file

@ -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"},