-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
12 changed files
with
310 additions
and
3 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,4 +1,5 @@ | ||
[ | ||
import_deps: [:ecto, :pluggable, :polymorphic_embed, :typed_struct], | ||
inputs: ["{.credo,.formatter,mix}.exs", "{config,lib,test}/**/*.{ex,exs}"] | ||
import_deps: [:ecto, :plug, :pluggable, :polymorphic_embed, :typed_struct], | ||
inputs: ["{.credo,.formatter,mix}.exs", "{config,lib,test}/**/*.{ex,exs}"], | ||
locals_without_parens: [rpc: 3] | ||
] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1 +1,3 @@ | ||
import Config | ||
|
||
config :resource_kit, ResourceKit.Endpoint, server: true |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,72 @@ | ||
defmodule ResourceKit.Controller.Actions do | ||
@moduledoc false | ||
|
||
alias ResourceKit.Schema.Ref | ||
alias ResourceKit.Schema.Request | ||
|
||
# TODO: remove this when deref is implemented | ||
@dialyzer {:no_match, fetch_action: 1} | ||
|
||
for type <- [:insert, :list] do | ||
@spec unquote(type)(request :: map(), ctx :: PhxJsonRpc.Router.Context.t()) :: map() | ||
def unquote(type)(request, _ctx) do | ||
with {:ok, request} <- cast_request(request), | ||
{:ok, action} <- fetch_action(request), | ||
{:ok, result} <- run(request, unquote(type), action) do | ||
result | ||
end | ||
end | ||
end | ||
|
||
defp cast_request(request) do | ||
request | ||
|> Request.changeset() | ||
|> Ecto.Changeset.apply_action(:insert) | ||
|> case do | ||
{:ok, request} -> {:ok, request} | ||
{:error, %Ecto.Changeset{errors: [reason | _rest]}} -> transform_error(reason) | ||
end | ||
end | ||
|
||
defp transform_error({:type = field, {"is invalid", options}}) do | ||
raise PhxJsonRpc.Error.InvalidParams, | ||
message: "#{field} is invalid", | ||
data: Keyword.fetch!(options, :enum) | ||
end | ||
|
||
defp transform_error({field, {"is invalid", _options}}) do | ||
raise PhxJsonRpc.Error.InvalidParams, message: "#{field} is invalid" | ||
end | ||
|
||
defp transform_error({field, {"can't be blank", validation: :required}}) do | ||
raise PhxJsonRpc.Error.InvalidParams, message: "#{field} is required" | ||
end | ||
|
||
defp fetch_action(%Request{uri: uri}) do | ||
case ResourceKit.Utils.deref(%Ref{uri: uri}) do | ||
{:ok, action} -> | ||
{:ok, action} | ||
|
||
{:error, {message, options}} -> | ||
raise PhxJsonRpc.Error.InvalidParams, | ||
message: "can't fetch action due to: '#{message}'", | ||
data: options | ||
end | ||
end | ||
|
||
defp run(%Request{params: params}, type, action) do | ||
case apply(ResourceKit, type, [action, params]) do | ||
{:ok, result} -> | ||
{:ok, result} | ||
|
||
{:error, {message, options}} -> | ||
raise PhxJsonRpc.Error.InternalError, | ||
message: "can't execute due to: #{message}", | ||
data: options | ||
|
||
{:error, %Ecto.Changeset{} = changeset} -> | ||
raise PhxJsonRpc.Error.InternalError, | ||
message: "can't execute due to: #{inspect(changeset)}" | ||
end | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
defmodule ResourceKit.Endpoint do | ||
@moduledoc false | ||
|
||
use Supervisor | ||
|
||
@spec start_link(args :: keyword()) :: Supervisor.on_start() | ||
def start_link(args) do | ||
Supervisor.start_link(__MODULE__, configuration(args), name: __MODULE__) | ||
end | ||
|
||
@impl Supervisor | ||
def init(args) do | ||
{server, options} = Keyword.pop(args, :server, false) | ||
children = if server, do: [{Bandit, options}], else: [] | ||
Supervisor.init(children, strategy: :one_for_one) | ||
end | ||
|
||
defp configuration(args) do | ||
:resource_kit | ||
|> Application.get_env(__MODULE__, []) | ||
|> Keyword.merge(args) | ||
|> Keyword.put(:plug, ResourceKit.Router) | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
defmodule ResourceKit.Router do | ||
@moduledoc false | ||
|
||
use Plug.Router | ||
|
||
plug Plug.Logger | ||
plug Plug.Parsers, parsers: [{:json, json_decoder: Jason}] | ||
plug :match | ||
plug :dispatch | ||
|
||
post "/rpc/actions", do: handle(conn, ResourceKit.Service.Actions) | ||
|
||
defp handle(%Plug.Conn{} = conn, service) do | ||
import PhxJsonRpcWeb.Views.Helpers | ||
|
||
conn | ||
|> put_resp_header("content-type", "application/json; charset=utf-8") | ||
|> send_json(conn.params |> service.handle() |> render_json()) | ||
end | ||
|
||
defp send_json(%Plug.Conn{} = conn, data) do | ||
send_resp(conn, 200, Jason.encode_to_iodata!(data)) | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
defmodule ResourceKit.Schema.Request do | ||
@moduledoc false | ||
|
||
use ResourceKit.Schema | ||
|
||
embedded_schema do | ||
field :uri, JetExt.Ecto.URI | ||
field :params, :map | ||
end | ||
|
||
@type t() :: %__MODULE__{ | ||
uri: URI.t(), | ||
params: map() | ||
} | ||
|
||
@spec changeset(schema :: %__MODULE__{}, params :: map()) :: Ecto.Changeset.t(t()) | ||
def changeset(schema \\ %__MODULE__{}, params) do | ||
schema | ||
|> Ecto.Changeset.cast(params, [:uri, :params]) | ||
|> Ecto.Changeset.validate_required([:uri, :params]) | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
defmodule ResourceKit.Service.Actions do | ||
@moduledoc false | ||
|
||
@dialyzer :no_behaviours | ||
|
||
use PhxJsonRpc.Router, | ||
max_batch_size: 10, | ||
otp_app: :resource_kit, | ||
schema: "priv/openrpc/services/actions.json", | ||
version: "2.0" | ||
|
||
rpc "insert", ResourceKit.Controller.Actions, :insert | ||
rpc "list", ResourceKit.Controller.Actions, :list | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,50 @@ | ||
{ | ||
"openrpc": "1.3.2", | ||
"info": { | ||
"title": "Actions", | ||
"version": "0.1.0" | ||
}, | ||
"methods": [ | ||
{ | ||
"name": "insert", | ||
"params": [ | ||
{ | ||
"$ref": "#/components/params/uri" | ||
}, | ||
{ | ||
"$ref": "#/components/params/params" | ||
} | ||
] | ||
}, | ||
{ | ||
"name": "list", | ||
"params": [ | ||
{ | ||
"$ref": "#/components/params/uri" | ||
}, | ||
{ | ||
"$ref": "#/components/params/params" | ||
} | ||
] | ||
} | ||
], | ||
"components": { | ||
"params": { | ||
"uri": { | ||
"name": "uri", | ||
"required": true, | ||
"schema": { | ||
"type": "string", | ||
"format": "uri" | ||
} | ||
}, | ||
"params": { | ||
"name": "params", | ||
"required": true, | ||
"schema": { | ||
"type": "object" | ||
} | ||
} | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,86 @@ | ||
defmodule ResourceKit.Controller.ActionsTest do | ||
use ResourceKit.Case.Database, async: true | ||
use ResourceKit.Case.Pipeline, async: true | ||
|
||
@uri "volume://action:deployment@/actions/movies/insert.json" | ||
@movie_name "movies" | ||
@movie_columns [ | ||
{:add, :id, :uuid, primary_key: true}, | ||
{:add, :title, :text, null: false}, | ||
{:add, :likes, :numeric, null: false}, | ||
{:add, :released, :boolean, null: false}, | ||
{:add, :release_date, :date, null: false}, | ||
{:add, :created_at, :timestamp, null: false}, | ||
{:add, :tags, {:array, :text}, null: false} | ||
] | ||
|
||
describe "cast request" do | ||
test "invalid uri" do | ||
assert_raise PhxJsonRpc.Error.InvalidParams, ~r|uri is required|, fn -> | ||
execute(:insert, %{params: %{}}) | ||
end | ||
|
||
assert_raise PhxJsonRpc.Error.InvalidParams, ~r|uri is invalid|, fn -> | ||
execute(:insert, %{uri: "://invalid", params: %{}}) | ||
end | ||
end | ||
|
||
test "invalid params" do | ||
assert_raise PhxJsonRpc.Error.InvalidParams, ~r|params is required|, fn -> | ||
execute(:insert, %{uri: @uri}) | ||
end | ||
|
||
assert_raise PhxJsonRpc.Error.InvalidParams, ~r|params is invalid|, fn -> | ||
execute(:insert, %{uri: @uri, params: []}) | ||
end | ||
end | ||
end | ||
|
||
describe "fetch action" do | ||
test "fails" do | ||
expect(ResourceKit.Utils, :deref, fn _ref -> {:error, {"does not exist", []}} end) | ||
|
||
assert_raise PhxJsonRpc.Error.InvalidParams, ~r|does not exist|, fn -> | ||
execute(:insert, %{ | ||
uri: "volume://action:deployment@/actions/resources/operate.json", | ||
params: %{} | ||
}) | ||
end | ||
end | ||
end | ||
|
||
describe "run" do | ||
setup :setup_tables | ||
setup :deref_json | ||
|
||
@tag [tables: [{@movie_name, @movie_columns}]] | ||
test "message" do | ||
uri = "volume://action:deployment@/actions/insert_movie.json" | ||
|
||
params = %{ | ||
"title" => "Spy x Family Code: White", | ||
"likes" => 2878, | ||
"released" => true, | ||
"release_date" => "2024-04-30", | ||
"created_at" => "2023-12-22T14:23:07Z", | ||
"tags" => ["Animation", "Comedy"] | ||
} | ||
|
||
assert %{ | ||
"title" => "Spy x Family Code: White", | ||
"likes" => %Decimal{coef: 2878}, | ||
"released" => true, | ||
"release_date" => ~D[2024-04-30], | ||
"created_at" => ~U[2023-12-22 14:23:07.000000Z], | ||
"tags" => ["Animation", "Comedy"] | ||
} = execute(:insert, %{uri: uri, params: params}) | ||
end | ||
end | ||
|
||
defp execute(type, request) do | ||
alias PhxJsonRpc.Router.Context | ||
alias ResourceKit.Controller.Actions | ||
|
||
apply(Actions, type, [request, Context.build(Actions)]) | ||
end | ||
end |