Skip to content

Commit 823e23d

Browse files
committed
Added support for actions' map
1 parent e9e6174 commit 823e23d

File tree

7 files changed

+198
-53
lines changed

7 files changed

+198
-53
lines changed

lib/kaffy/resource_form.ex

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -336,7 +336,9 @@ defmodule Kaffy.ResourceForm do
336336
{nil, ""}
337337

338338
messages ->
339-
error_msg = Kaffy.ResourceAdmin.humanize_term(field) <> " " <> Enum.join(messages, ", ") <> "!"
339+
error_msg =
340+
Kaffy.ResourceAdmin.humanize_term(field) <> " " <> Enum.join(messages, ", ") <> "!"
341+
340342
{error_msg, "is-invalid"}
341343
end
342344
end
@@ -349,7 +351,9 @@ defmodule Kaffy.ResourceForm do
349351
end)
350352
end
351353

352-
defp build_changeset_value(value) when is_tuple(value), do: value |> Tuple.to_list() |> Enum.join(", ")
354+
defp build_changeset_value(value) when is_tuple(value),
355+
do: value |> Tuple.to_list() |> Enum.join(", ")
356+
353357
defp build_changeset_value(value), do: to_string(value)
354358

355359
def kaffy_input(conn, changeset, form, field, options) do

lib/kaffy/resource_query.ex

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -102,7 +102,7 @@ defmodule Kaffy.ResourceQuery do
102102

103103
query =
104104
cond do
105-
(is_nil(search_fields) || Enum.empty?(search_fields)) || search == "" ->
105+
is_nil(search_fields) || Enum.empty?(search_fields) || search == "" ->
106106
query
107107

108108
true ->
@@ -118,7 +118,9 @@ defmodule Kaffy.ResourceQuery do
118118
query = from(s in q, join: a in assoc(s, ^association))
119119

120120
Enum.reduce(fields, query, fn f, current_query ->
121-
from([..., r] in current_query, or_where: ilike(type(field(r, ^f), :string), ^term))
121+
from([..., r] in current_query,
122+
or_where: ilike(type(field(r, ^f), :string), ^term)
123+
)
122124
end)
123125

124126
f, q ->

lib/kaffy_web/controllers/resource_controller.ex

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -321,8 +321,7 @@ defmodule KaffyWeb.ResourceController do
321321
my_resource = Kaffy.Utils.get_resource(conn, context, resource)
322322
entry = Kaffy.ResourceQuery.fetch_resource(conn, my_resource, id)
323323
actions = Kaffy.ResourceAdmin.resource_actions(my_resource, conn)
324-
action_key = String.to_existing_atom(action_key)
325-
[action_record] = Keyword.get_values(actions, action_key)
324+
action_record = get_action_record(actions, action_key)
326325

327326
case action_record.action.(conn, entry) do
328327
{:ok, entry} ->
@@ -344,11 +343,10 @@ defmodule KaffyWeb.ResourceController do
344343
%{"context" => context, "resource" => resource, "action_key" => action_key} = params
345344
) do
346345
my_resource = Kaffy.Utils.get_resource(conn, context, resource)
347-
action_key = String.to_existing_atom(action_key)
348346
ids = Map.get(params, "ids", "") |> String.split(",")
349347
entries = Kaffy.ResourceQuery.fetch_list(my_resource, ids)
350348
actions = Kaffy.ResourceAdmin.list_actions(my_resource, conn)
351-
[action_record] = Keyword.get_values(actions, action_key)
349+
action_record = get_action_record(actions, action_key)
352350
kaffy_inputs = Map.get(params, "kaffy-input", %{})
353351

354352
result =
@@ -394,4 +392,16 @@ defmodule KaffyWeb.ResourceController do
394392
)
395393
)
396394
end
395+
396+
# we received actions as map so we actually use strings as keys
397+
defp get_action_record(actions, action_key) when is_map(actions) and is_binary(action_key) do
398+
Map.fetch!(actions, action_key)
399+
end
400+
401+
# we received actions as Keyword list so action_key needs to be an atom
402+
defp get_action_record(actions, action_key) when is_list(actions) and is_binary(action_key) do
403+
action_key = String.to_existing_atom(action_key)
404+
[action_record] = Keyword.get_values(actions, action_key)
405+
action_record
406+
end
397407
end

mix.exs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ defmodule Kaffy.MixProject do
3232
[
3333
{:phoenix, "~> 1.4"},
3434
{:phoenix_html, "~> 2.11"},
35+
{:mock, "~> 0.3.0", only: :test},
3536
{:ecto, "~> 3.0"},
3637
{:ex_doc, ">= 0.0.0", only: :dev, runtime: false}
3738
]

mix.lock

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,9 @@
88
"jumper": {:hex, :jumper, "1.0.1", "3c00542ef1a83532b72269fab9f0f0c82bf23a35e27d278bfd9ed0865cecabff", [:mix], [], "hexpm", "318c59078ac220e966d27af3646026db9b5a5e6703cb2aa3e26bcfaba65b7433"},
99
"makeup": {:hex, :makeup, "1.0.1", "82f332e461dc6c79dbd82fbe2a9c10d48ed07146f0a478286e590c83c52010b5", [:mix], [{:nimble_parsec, "~> 0.5.0", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "49736fe5b66a08d8575bf5321d716bac5da20c8e6b97714fec2bcd6febcfa1f8"},
1010
"makeup_elixir": {:hex, :makeup_elixir, "0.14.0", "cf8b7c66ad1cff4c14679698d532f0b5d45a3968ffbcbfd590339cb57742f1ae", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}], "hexpm", "d4b316c7222a85bbaa2fd7c6e90e37e953257ad196dc229505137c5e505e9eff"},
11+
"meck": {:hex, :meck, "0.8.13", "ffedb39f99b0b99703b8601c6f17c7f76313ee12de6b646e671e3188401f7866", [:rebar3], [], "hexpm", "d34f013c156db51ad57cc556891b9720e6a1c1df5fe2e15af999c84d6cebeb1a"},
1112
"mime": {:hex, :mime, "1.3.1", "30ce04ab3175b6ad0bdce0035cba77bba68b813d523d1aac73d9781b4d193cf8", [:mix], [], "hexpm", "6cbe761d6a0ca5a31a0931bf4c63204bceb64538e664a8ecf784a9a6f3b875f1"},
13+
"mock": {:hex, :mock, "0.3.6", "e810a91fabc7adf63ab5fdbec5d9d3b492413b8cda5131a2a8aa34b4185eb9b4", [:mix], [{:meck, "~> 0.8.13", [hex: :meck, repo: "hexpm", optional: false]}], "hexpm", "bcf1d0a6826fb5aee01bae3d74474669a3fa8b2df274d094af54a25266a1ebd2"},
1214
"nimble_parsec": {:hex, :nimble_parsec, "0.5.3", "def21c10a9ed70ce22754fdeea0810dafd53c2db3219a0cd54cf5526377af1c6", [:mix], [], "hexpm", "589b5af56f4afca65217a1f3eb3fee7e79b09c40c742fddc1c312b3ac0b3399f"},
1315
"phoenix": {:hex, :phoenix, "1.4.17", "1b1bd4cff7cfc87c94deaa7d60dd8c22e04368ab95499483c50640ef3bd838d8", [:mix], [{:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:phoenix_pubsub, "~> 1.1", [hex: :phoenix_pubsub, repo: "hexpm", optional: false]}, {:plug, "~> 1.8.1 or ~> 1.9", [hex: :plug, repo: "hexpm", optional: false]}, {:plug_cowboy, "~> 1.0 or ~> 2.0", [hex: :plug_cowboy, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "3a8e5d7a3d76d452bb5fb86e8b7bd115f737e4f8efe202a463d4aeb4a5809611"},
1416
"phoenix_html": {:hex, :phoenix_html, "2.14.2", "b8a3899a72050f3f48a36430da507dd99caf0ac2d06c77529b1646964f3d563e", [:mix], [{:plug, "~> 1.5", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm", "58061c8dfd25da5df1ea0ca47c972f161beb6c875cd293917045b92ffe1bf617"},

test/actions_test.exs

Lines changed: 143 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,143 @@
1+
defmodule ActionsTest do
2+
use ExUnit.Case
3+
use Phoenix.ConnTest
4+
5+
import Mock
6+
alias Phoenix.Controller, as: PhoenixController
7+
8+
alias KaffyWeb.ResourceController
9+
10+
defmodule ActionsListAdmin do
11+
@atom_action :test_action
12+
@response %{id: "id"}
13+
14+
def index(_), do: []
15+
16+
def resource_actions(_conn) do
17+
[
18+
{@atom_action,
19+
%{
20+
name: "Test Action",
21+
action: fn _, _ ->
22+
{:ok, @response}
23+
end
24+
}}
25+
]
26+
end
27+
28+
def list_actions(_conn) do
29+
[
30+
{@atom_action,
31+
%{
32+
name: "Test Action",
33+
action: fn _, _, _ ->
34+
{:ok, @response}
35+
end
36+
}}
37+
]
38+
end
39+
end
40+
41+
defmodule ActionsMapAdmin do
42+
@string_action "test_action"
43+
@response %{id: "id"}
44+
45+
def index(_), do: []
46+
47+
def resource_actions(_conn) do
48+
%{
49+
@string_action => %{
50+
name: "Test Action",
51+
action: fn _, _ ->
52+
{:ok, @response}
53+
end
54+
}
55+
}
56+
end
57+
58+
def list_actions(_conn) do
59+
%{
60+
@string_action => %{
61+
name: "Test Action",
62+
action: fn _, _, _ ->
63+
{:ok, @response}
64+
end
65+
}
66+
}
67+
end
68+
end
69+
70+
defmodule FakeSchema do
71+
use Ecto.Schema
72+
73+
schema "fake_schema" do
74+
end
75+
76+
def changeset(fake, _attrs) do
77+
fake
78+
end
79+
end
80+
81+
defmodule FakeRouter do
82+
use Phoenix.Router
83+
use Kaffy.Routes, scope: "/admin"
84+
end
85+
86+
setup do
87+
Application.put_env(:kaffy, :resources, fn _ ->
88+
[
89+
list: [
90+
# a custom name for this context/section.
91+
name: "list",
92+
# this line used to be "schemas" in pre v0.9
93+
resources: [
94+
test: [schema: FakeSchema, admin: ActionsListAdmin]
95+
]
96+
],
97+
map: [
98+
name: "map",
99+
resources: [
100+
test: [schema: FakeSchema, admin: ActionsMapAdmin]
101+
]
102+
]
103+
]
104+
end)
105+
106+
Application.put_env(:kaffy, :router, FakeRouter)
107+
108+
conn =
109+
build_conn()
110+
|> Plug.Test.init_test_session(%{})
111+
|> PhoenixController.fetch_flash([])
112+
113+
[conn: conn]
114+
end
115+
116+
test "resource controller accepts list of actions", %{conn: conn} do
117+
with_mock Kaffy.ResourceQuery, fetch_resource: fn _, _, _ -> %{id: "id"} end do
118+
result_conn =
119+
ResourceController.single_action(conn, %{
120+
"context" => "list",
121+
"resource" => "test",
122+
"action_key" => "test_action",
123+
"id" => "id"
124+
})
125+
126+
assert %{"success" => _} = get_flash(result_conn)
127+
end
128+
end
129+
130+
test "resource controller accepts map of actions", %{conn: conn} do
131+
with_mock Kaffy.ResourceQuery, fetch_resource: fn _, _, _ -> %{id: "id"} end do
132+
result_conn =
133+
ResourceController.single_action(conn, %{
134+
"context" => "map",
135+
"resource" => "test",
136+
"action_key" => "test_action",
137+
"id" => "id"
138+
})
139+
140+
assert %{"success" => _} = get_flash(result_conn)
141+
end
142+
end
143+
end

test/kaffy_test.exs

Lines changed: 28 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -20,92 +20,75 @@ defmodule KaffyTest do
2020
assert is_nil(pet.name)
2121
end
2222

23-
describe "Kaffy.Resource" do
24-
alias Kaffy.Resource
23+
describe "Kaffy.ResourceSchema" do
24+
alias Kaffy.ResourceSchema
2525

2626
test "excluded_fields should return primary keys" do
27-
assert [:id] == Resource.excluded_fields(Person)
28-
assert [:id] == Resource.excluded_fields(Pet)
27+
assert [:id] == ResourceSchema.excluded_fields(Person)
28+
assert [:id] == ResourceSchema.excluded_fields(Pet)
2929
end
3030

31-
test "primary_keys/1 should return a list of primary keys" do
32-
assert [:id] == Resource.primary_keys(Person)
33-
assert [:id] == Resource.primary_keys(Pet)
31+
test "primary_key/1 should return a primary key" do
32+
assert [:id] == ResourceSchema.primary_key(Person)
33+
assert [:id] == ResourceSchema.primary_key(Pet)
3434
end
3535

3636
test "kaffy_field_name/2 should return the name of the field" do
37-
assert "Address" == Resource.kaffy_field_name(nil, :address)
38-
assert "Created_at" == Resource.kaffy_field_name(nil, :created_at)
37+
assert "Address" == ResourceSchema.kaffy_field_name(nil, :address)
38+
assert "Created At" == ResourceSchema.kaffy_field_name(nil, :created_at)
3939
person = %Person{name: "Abdullah"}
4040
f = {:status, %{name: "Yes"}}
41-
assert "Yes" == Resource.kaffy_field_name(person, f)
41+
assert "Yes" == ResourceSchema.kaffy_field_name(person, f)
4242
f = {:status, %{name: fn p -> String.upcase(p.name) end}}
43-
assert "ABDULLAH" == Resource.kaffy_field_name(person, f)
43+
assert "ABDULLAH" == ResourceSchema.kaffy_field_name(person, f)
4444
f = {:status, %{value: "something"}}
45-
assert "Status" == Resource.kaffy_field_name(person, f)
45+
assert "Status" == ResourceSchema.kaffy_field_name(person, f)
4646
end
4747

4848
test "kaffy_field_value/2 should return the value of the field" do
4949
person = %Person{name: "Abdullah"}
50-
assert "Abdullah" == Resource.kaffy_field_value(person, :name)
50+
assert "Abdullah" == ResourceSchema.kaffy_field_value(person, :name)
5151
field = {:name, %{value: "Esmail"}}
52-
assert "Esmail" == Resource.kaffy_field_value(person, field)
52+
assert "Esmail" == ResourceSchema.kaffy_field_value(nil, person, field)
5353
field = {:name, %{value: fn p -> "Mr. #{p.name}" end}}
54-
assert "Mr. Abdullah" == Resource.kaffy_field_value(person, field)
54+
assert "Mr. Abdullah" == ResourceSchema.kaffy_field_value(nil, person, field)
5555
field = {:name, %{name: fn p -> "Mr. #{p.name}" end}}
56-
assert "Abdullah" == Resource.kaffy_field_value(person, field)
57-
end
58-
59-
test "fields/1 should return all the fields of a schema without associations" do
60-
fields = Resource.fields(Person)
61-
assert is_list(fields)
62-
assert Enum.all?(fields, fn f -> is_atom(f) end)
63-
[first | _] = fields
64-
assert first == :id
65-
assert length(fields) == 6
56+
assert "Abdullah" == ResourceSchema.kaffy_field_value(nil, person, field)
6657
end
6758

6859
test "associations/1 must return all associations for the schema" do
69-
associations = Resource.associations(Person)
60+
associations = ResourceSchema.associations(Person)
7061
assert [:pets] == associations
71-
pet_assoc = Resource.associations(Pet)
62+
pet_assoc = ResourceSchema.associations(Pet)
7263
assert [:person] == pet_assoc
7364
end
7465

7566
test "association/1 must return information about the association" do
76-
person_assoc = Resource.association(Person, :pets)
67+
person_assoc = ResourceSchema.association(Person, :pets)
7768
assert Ecto.Association.Has == person_assoc.__struct__
7869
assert person_assoc.cardinality == :many
7970
assert person_assoc.queryable == Pet
8071

81-
pet_assoc = Resource.association(Pet, :person)
72+
pet_assoc = ResourceSchema.association(Pet, :person)
8273
assert Ecto.Association.BelongsTo == pet_assoc.__struct__
8374
assert pet_assoc.cardinality == :one
8475
assert pet_assoc.queryable == Person
8576
end
8677

8778
test "association_schema/2 must return the schema of the association" do
88-
assert Pet == Resource.association_schema(Person, :pets)
89-
assert Person == Resource.association_schema(Pet, :person)
90-
end
91-
92-
test "form_label/2 should return a label tag" do
93-
{:safe, label_tag} = Resource.form_label(:user, "My name")
94-
assert is_list(label_tag)
95-
label_string = to_string(label_tag)
96-
assert String.contains?(label_string, "<label")
97-
assert String.contains?(label_string, "user")
98-
assert String.contains?(label_string, "My name")
79+
assert Pet == ResourceSchema.association_schema(Person, :pets)
80+
assert Person == ResourceSchema.association_schema(Pet, :person)
9981
end
10082
end
10183

10284
describe "Kaffy.ResourceAdmin" do
10385
alias Kaffy.ResourceAdmin
10486

105-
test "index/1 should return a keyword list of fields and their values" do
106-
assert Kaffy.Resource.fields(Person) == ResourceAdmin.index(schema: Person)
107-
custom_index = ResourceAdmin.index(schema: Person, admin: PersonAdmin)
108-
assert [:name, :married] == Keyword.keys(custom_index)
109-
end
87+
# [Qizot] I don't know if this test should be valid anymore if associations are allowed
88+
# test "index/1 should return a keyword list of fields and their values" do
89+
# assert Kaffy.ResourceSchema.fields(Person) == ResourceAdmin.index(schema: Person)
90+
# custom_index = ResourceAdmin.index(schema: Person, admin: PersonAdmin)
91+
# assert [:name, :married] == Keyword.keys(custom_index)
92+
# end
11093
end
11194
end

0 commit comments

Comments
 (0)