mirror of
https://github.com/ash-project/ash_json_api_wrapper.git
synced 2024-09-19 12:53:10 +12:00
WIP
This commit is contained in:
parent
b541a64482
commit
de000a859c
5 changed files with 8903 additions and 2 deletions
|
@ -193,6 +193,8 @@ defmodule AshJsonApiWrapper.DataLayer do
|
|||
),
|
||||
do: true
|
||||
|
||||
def can?(_, :nested_expressions), do: true
|
||||
|
||||
def can?(_, :sort), do: true
|
||||
def can?(_, {:sort, _}), do: true
|
||||
def can?(_, _), do: false
|
||||
|
@ -361,9 +363,11 @@ defmodule AshJsonApiWrapper.DataLayer do
|
|||
end
|
||||
end
|
||||
|
||||
defp filter_instructions(filter, _resource, endpoint) do
|
||||
defp filter_instructions(filter, resource, endpoint) do
|
||||
fields =
|
||||
endpoint.fields
|
||||
|> Enum.concat(AshJsonApiWrapper.DataLayer.Info.fields(resource))
|
||||
|> Enum.uniq_by(& &1.name)
|
||||
|> List.wrap()
|
||||
|> Enum.filter(& &1.filter_handler)
|
||||
|
||||
|
@ -691,6 +695,8 @@ defmodule AshJsonApiWrapper.DataLayer do
|
|||
|
||||
defp make_request(resource, query) do
|
||||
# log_send(path, query)
|
||||
IO.inspect(query.query_params)
|
||||
|
||||
AshJsonApiWrapper.DataLayer.Info.tesla(resource).get(query.path,
|
||||
body: query.body,
|
||||
query: query.query_params
|
||||
|
@ -794,6 +800,7 @@ defmodule AshJsonApiWrapper.DataLayer do
|
|||
else
|
||||
case endpoint.entity_path do
|
||||
nil ->
|
||||
IO.inspect(endpoint)
|
||||
{:ok, List.wrap(body)}
|
||||
|
||||
path ->
|
||||
|
|
|
@ -7,7 +7,7 @@ defmodule AshJsonApiWrapper.DataLayer.Transformers.SetEndpointDefaults do
|
|||
def transform(dsl) do
|
||||
base_entity_path = AshJsonApiWrapper.DataLayer.Info.base_entity_path(dsl)
|
||||
base_paginator = AshJsonApiWrapper.DataLayer.Info.base_paginator(dsl)
|
||||
base_fields = AshJsonApiWrapper.DataLayer.Info.fields(dsl)
|
||||
base_fields = AshJsonApiWrapper.DataLayer.Info.fields(dsl) |> IO.inspect()
|
||||
|
||||
dsl
|
||||
|> AshJsonApiWrapper.DataLayer.Info.endpoints()
|
||||
|
|
206
lib/open_api/resource_generator.ex
Normal file
206
lib/open_api/resource_generator.ex
Normal file
|
@ -0,0 +1,206 @@
|
|||
defmodule AshJsonApiWrapper.OpenApi.ResourceGenerator do
|
||||
def generate(json, main_config) do
|
||||
main_config[:resources]
|
||||
|> Enum.map(fn {resource, config} ->
|
||||
endpoints =
|
||||
json
|
||||
|> operations(config)
|
||||
|> Enum.take(1)
|
||||
|> Enum.map_join("\n\n", fn {path, method, operation} ->
|
||||
entity_path =
|
||||
if config[:entity_path] do
|
||||
"entity_path \"#{config[:entity_path]}\""
|
||||
end
|
||||
|
||||
"""
|
||||
endpoint :#{operation_id(operation)} do
|
||||
path "#{path}"
|
||||
#{entity_path}
|
||||
end
|
||||
"""
|
||||
end)
|
||||
|
||||
actions =
|
||||
json
|
||||
|> operations(config)
|
||||
|> Enum.take(1)
|
||||
|> Enum.map_join("\n\n", fn
|
||||
{path, "get", config} ->
|
||||
"""
|
||||
read :#{operation_id(config)}
|
||||
"""
|
||||
|
||||
{path, "post", config} ->
|
||||
"""
|
||||
create :#{operation_id(config)}
|
||||
"""
|
||||
end)
|
||||
|
||||
fields =
|
||||
config[:fields]
|
||||
|> Enum.map_join("\n\n", fn {name, field_config} ->
|
||||
filter_handler =
|
||||
if field_config[:filter_handler] do
|
||||
"filter_handler #{inspect(field_config[:filter_handler])}"
|
||||
end
|
||||
|
||||
"""
|
||||
field #{inspect(name)} do
|
||||
#{filter_handler}
|
||||
end
|
||||
"""
|
||||
end)
|
||||
|> case do
|
||||
"" ->
|
||||
""
|
||||
|
||||
other ->
|
||||
"""
|
||||
fields do
|
||||
#{other}
|
||||
end
|
||||
"""
|
||||
end
|
||||
|
||||
{:ok, [object]} =
|
||||
json
|
||||
|> ExJSONPath.eval(config[:object_type])
|
||||
|
||||
attributes =
|
||||
object
|
||||
|> Map.get("properties")
|
||||
|> Enum.map(fn {name, config} ->
|
||||
{Macro.underscore(name), config}
|
||||
end)
|
||||
|> Enum.sort_by(fn {name, _} ->
|
||||
name not in List.wrap(config[:primary_key])
|
||||
end)
|
||||
|> Enum.map_join("\n\n", fn {name, property} ->
|
||||
type =
|
||||
case property do
|
||||
%{"enum" => values} ->
|
||||
":atom"
|
||||
|
||||
%{"format" => "date-time"} ->
|
||||
":utc_datetime"
|
||||
|
||||
%{"type" => "string"} ->
|
||||
":string"
|
||||
|
||||
%{"type" => "integer"} ->
|
||||
":integer"
|
||||
|
||||
other ->
|
||||
raise "Unsupported property: #{inspect(other)}"
|
||||
end
|
||||
|
||||
constraints =
|
||||
case property do
|
||||
%{"enum" => values} ->
|
||||
"one_of: #{inspect(Enum.map(values, &String.to_atom/1))}"
|
||||
|
||||
%{"maxLength" => max, "minLength" => min, "type" => "string"} ->
|
||||
"min_length: #{min}, max_length: #{max}"
|
||||
|
||||
%{"maxLength" => max, "type" => "string"} ->
|
||||
"max_length: #{max}"
|
||||
|
||||
%{"minLength" => min, "type" => "string"} ->
|
||||
"min_length: #{min}"
|
||||
|
||||
_ ->
|
||||
nil
|
||||
end
|
||||
|
||||
primary_key? = name in List.wrap(config[:primary_key])
|
||||
|
||||
if constraints || primary_key? do
|
||||
constraints =
|
||||
if constraints do
|
||||
"constraints #{constraints}"
|
||||
end
|
||||
|
||||
primary_key =
|
||||
if primary_key? do
|
||||
"""
|
||||
primary_key? true
|
||||
allow_nil? false
|
||||
"""
|
||||
end
|
||||
|
||||
"""
|
||||
attribute :#{name}, #{type} do
|
||||
#{primary_key}
|
||||
#{constraints}
|
||||
end
|
||||
"""
|
||||
else
|
||||
"""
|
||||
attribute :#{name}, #{type}
|
||||
"""
|
||||
end
|
||||
end)
|
||||
|
||||
tesla =
|
||||
if main_config[:tesla] do
|
||||
"tesla #{main_config[:tesla]}"
|
||||
end
|
||||
|
||||
endpoint =
|
||||
if main_config[:endpoint] do
|
||||
"base \"#{main_config[:endpoint]}\""
|
||||
end
|
||||
|
||||
code =
|
||||
"""
|
||||
defmodule #{resource} do
|
||||
use Ash.Resource, data_layer: AshJsonApiWrapper.DataLayer
|
||||
|
||||
json_api_wrapper do
|
||||
#{tesla}
|
||||
|
||||
endpoints do
|
||||
#{endpoint}
|
||||
#{endpoints}
|
||||
end
|
||||
|
||||
#{fields}
|
||||
end
|
||||
|
||||
actions do
|
||||
#{actions}
|
||||
end
|
||||
|
||||
attributes do
|
||||
#{attributes}
|
||||
end
|
||||
end
|
||||
"""
|
||||
|> Code.format_string!()
|
||||
|> IO.iodata_to_binary()
|
||||
|
||||
{resource, code}
|
||||
end)
|
||||
|> Enum.take(1)
|
||||
end
|
||||
|
||||
defp operation_id(%{"operationId" => operationId}) do
|
||||
operationId
|
||||
|> Macro.underscore()
|
||||
end
|
||||
|
||||
defp operations(json, config) do
|
||||
json["paths"]
|
||||
|> Enum.filter(fn {path, _value} ->
|
||||
String.starts_with?(path, config[:path])
|
||||
end)
|
||||
|> Enum.flat_map(fn {path, methods} ->
|
||||
Enum.map(methods, fn {method, config} ->
|
||||
{path, method, config}
|
||||
end)
|
||||
end)
|
||||
|> Enum.filter(fn {_path, method, _config} ->
|
||||
method in ["get", "post"]
|
||||
end)
|
||||
end
|
||||
end
|
86
test/open_api_test.exs
Normal file
86
test/open_api_test.exs
Normal file
|
@ -0,0 +1,86 @@
|
|||
defmodule AshJsonApiWrapper.Hackernews.Test do
|
||||
use ExUnit.Case
|
||||
|
||||
require Ash.Query
|
||||
|
||||
@json "test/support/cybrid.json" |> File.read!() |> Jason.decode!()
|
||||
|
||||
defmodule TestingTesla do
|
||||
use Tesla
|
||||
|
||||
plug(Tesla.Middleware.Headers, [
|
||||
{"authorization",
|
||||
"Bearer eyJraWQiOiJTcWZRWXNjelFQbENOSDhxOXZuR1E2WWcwQk1ENm5UZkZMLWhxeER6eFdFIiwiYWxnIjoiUlM1MTIifQ.eyJpc3MiOiJodHRwczovL2lkLnNhbmRib3guY3licmlkLmFwcCIsImF1ZCI6WyJodHRwczovL2Jhbmsuc2FuZGJveC5jeWJyaWQuYXBwIiwiaHR0cDovL3NhbmRib3gtYXBpLWludGVybmFsLWtleTozMDA1IiwiaHR0cHM6Ly9pZC5zYW5kYm94LmN5YnJpZC5hcHAiLCJodHRwOi8vc2FuZGJveC1hcGktaW50ZXJuYWwtYWNjb3VudHM6MzAwMyIsImh0dHA6Ly9zYW5kYm94LWFwaS1pbnRlcm5hbC1pZGVudGl0eTozMDA0IiwiaHR0cDovL3NhbmRib3gtYXBpLWludGVncmF0aW9uLWV4Y2hhbmdlOjMwMDYiLCJodHRwOi8vc2FuZGJveC1hcGktaW50ZWdyYXRpb24tdHJhbnNmZXJzOjMwMDciXSwic3ViIjoiODVjZTljNDgxYjNiN2MwM2YwMTMxOTQ0MjVmODc5MmEiLCJzdWJfdHlwZSI6ImJhbmsiLCJzY29wZSI6WyJiYW5rczpyZWFkIiwiYmFua3M6d3JpdGUiLCJhY2NvdW50czpyZWFkIiwiYWNjb3VudHM6ZXhlY3V0ZSIsImN1c3RvbWVyczpyZWFkIiwiY3VzdG9tZXJzOndyaXRlIiwiY3VzdG9tZXJzOmV4ZWN1dGUiLCJwcmljZXM6cmVhZCIsInF1b3RlczpleGVjdXRlIiwicXVvdGVzOnJlYWQiLCJ0cmFkZXM6ZXhlY3V0ZSIsInRyYWRlczpyZWFkIiwidHJhbnNmZXJzOmV4ZWN1dGUiLCJ0cmFuc2ZlcnM6cmVhZCIsInJld2FyZHM6ZXhlY3V0ZSIsInJld2FyZHM6cmVhZCIsImV4dGVybmFsX2JhbmtfYWNjb3VudHM6cmVhZCIsImV4dGVybmFsX2JhbmtfYWNjb3VudHM6d3JpdGUiLCJleHRlcm5hbF9iYW5rX2FjY291bnRzOmV4ZWN1dGUiLCJleHRlcm5hbF93YWxsZXRzOnJlYWQiLCJleHRlcm5hbF93YWxsZXRzOmV4ZWN1dGUiLCJ3b3JrZmxvd3M6cmVhZCIsIndvcmtmbG93czpleGVjdXRlIiwiZGVwb3NpdF9hZGRyZXNzZXM6cmVhZCIsImRlcG9zaXRfYWRkcmVzc2VzOmV4ZWN1dGUiXSwiaWF0IjoxNjg4MTU4MTE4LCJleHAiOjE2ODgxODY5MTgsImp0aSI6Ijg0ODNhOTg3LWEzZjQtNGU4Mi1iODc3LTg5MDk4YTIyYWE4ZSIsInRva2VuX3R5cGUiOiJhY2Nlc3MiLCJwcm9wZXJ0aWVzIjp7InR5cGUiOiJzYW5kYm94In19.SVFHoZWIKP-owEYzOSfP53nW9oM068t5-CUkPUIlXWPmV_rPTNGhaqjdy9u7iZQvXZX2BF5_gJHx1QR91DBYoR0ftRHxsQTq4UsJChTPfIEZZPuA_lf2iOSy-ivtEdXgqGGHnuItxzS-NnadffSawNXK8Em2Dhfwq7eLps6KvE6fVGelpinvTbfMD7L9PbCdNLdoonEbkdG6eMDV8FEX0sDJhfEd_GUp_HzAKFZwiK_g7NTT4rgm_0Yp6Paue3_ZviDpEWCLhyQNxd-N2TlP4wQng3zafB9_JPX3Z-xKq2WU5z_VltOTHcCMrvsDhDA2oI1CgFT92LMQmC_3QlpCVyaN70Jpd2E-ON9TehQ6JjcNZXoiKl7YaoGDadrAOXdYacexvsNPRpxZhZYxKX3FWtUxYeg0mIHNSS3nd14kfBXVARIqGuBYzRjepmb49MJERNzdeQ-3YectmBVWsPFWfnuMZfWUW54yHR0EF-oWLJJhBqUaZTysyXWpLeKnTBb2t6Q0y9_GLltxZh4x44qRmaq7k511QkEcBbLOnR40HqwnoteCQs-Yqnc8nwHBZ8H6gkUWtQTDiu4_uOmqoqXDx9WDuX4z5pE4M8HzzC1nu7-KvCcqAaCgVQV_Nut0V_IwE-6vB3JOIbFLGjHGMK_WwOrCefKbLoH6ZN6v7wz0cuY"}
|
||||
])
|
||||
end
|
||||
|
||||
@config [
|
||||
tesla: TestingTesla,
|
||||
endpoint: "https://bank.sandbox.cybrid.app",
|
||||
resources: [
|
||||
"Cybrid.Account": [
|
||||
path: "/api/accounts",
|
||||
object_type: "components.schemas.Account",
|
||||
primary_key: "guid",
|
||||
entity_path: "objects",
|
||||
fields: [
|
||||
guid: [
|
||||
filter_handler: {:place_in_list, ["guid"]}
|
||||
# {
|
||||
# "name": "guid",
|
||||
# "in": "query",
|
||||
# "required": false,
|
||||
# "description": "Comma separated account_guids to list accounts for.",
|
||||
# "schema": {
|
||||
# "type": "string"
|
||||
# }
|
||||
# },
|
||||
# {
|
||||
# "name": "bank_guid",
|
||||
# "in": "query",
|
||||
# "required": false,
|
||||
# "description": "Comma separated bank_guids to list accounts for.",
|
||||
# "schema": {
|
||||
# "type": "string"
|
||||
# }
|
||||
# },
|
||||
# {
|
||||
# "name": "customer_guid",
|
||||
# "in": "query",
|
||||
# "required": false,
|
||||
# "description": "Comma separated customer_guids to list accounts for.",
|
||||
# "schema": {
|
||||
# "type": "string"
|
||||
# }
|
||||
# }
|
||||
]
|
||||
]
|
||||
]
|
||||
]
|
||||
]
|
||||
|
||||
defmodule Api do
|
||||
use Ash.Api
|
||||
|
||||
resources do
|
||||
allow_unregistered? true
|
||||
end
|
||||
end
|
||||
|
||||
test "it does stuff" do
|
||||
@json
|
||||
|> AshJsonApiWrapper.OpenApi.ResourceGenerator.generate(@config)
|
||||
|> Enum.map(fn {resource, code} ->
|
||||
IO.puts(code)
|
||||
Code.eval_string(code)
|
||||
resource
|
||||
end)
|
||||
|
||||
Cybrid.Account
|
||||
|> Ash.Query.for_read(:list_accounts)
|
||||
|> Ash.Query.filter(guid == "1c96166bfa20e434962d6f08a96e69ad")
|
||||
# |> Ash.Query.filter(type == :fee)
|
||||
|> Api.read!()
|
||||
|> IO.inspect()
|
||||
end
|
||||
end
|
8602
test/support/cybrid.json
Normal file
8602
test/support/cybrid.json
Normal file
File diff suppressed because one or more lines are too long
Loading…
Reference in a new issue