Skip to content

Commit 3fb1d26

Browse files
committed
Add tests for menus
1 parent 44f1873 commit 3fb1d26

File tree

5 files changed

+476
-60
lines changed

5 files changed

+476
-60
lines changed

lib/sanbase/menu/menus.ex

Lines changed: 75 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ defmodule Sanbase.Menus do
5656
query = Menu.by_id(menu_id, user_id)
5757

5858
case Repo.one(query) do
59-
nil -> {:error, "Menu with id #{menu_id} not found"}
59+
nil -> {:error, "Menu with id #{menu_id} not found or it is owned by another user"}
6060
menu -> {:ok, menu}
6161
end
6262
end
@@ -65,10 +65,14 @@ defmodule Sanbase.Menus do
6565
Convert a menu with preloaded menu items to a map in the format. This format
6666
can directly be returned by the GraphQL API if the return type is `:json`
6767
68+
Note: The keys are strings in camelCase, not atoms in snake case. This is because this result
69+
is directly returned to the API client as a JSON type, which does not go through the
70+
snake_case => camelCase transformation.
71+
6872
%{
69-
entity: :menu, id: 1, name: "N", description: "D", menu_items: [
70-
%{entity_type: :query, id: 2, name: "Q", description: "D", position: 1},
71-
%{entity_type: :dashboard, id: 21, name: "D", description: "D", position: 2}
73+
"entityType" => :menu, "entityId" 1, "name" => "N", "description" => "D", "menuItems" => [
74+
%{"entityType" => :query, "entityType" => 2, "name" => "Q", "description" => "D", "position" => 1},
75+
%{"entityType" => :dashboard, "entityType" => 21, "name" => "D", "description" => "D", "position" => 2}
7276
]
7377
}
7478
"""
@@ -77,12 +81,12 @@ defmodule Sanbase.Menus do
7781
# If this menu is a sub-menu, then the caller from get_menu_items/1 will
7882
# additionally set the menu_item_id. If this is the top-level menu, then
7983
# this is not a sub-menu and it does not have a menu_item_id
80-
menu_item_id: nil,
81-
type: :menu,
82-
id: menu.id,
83-
name: menu.name,
84-
description: menu.description,
85-
menu_items: get_menu_items(menu)
84+
"menuItemId" => nil,
85+
"entityType" => :menu,
86+
"entityId" => menu.id,
87+
"name" => menu.name,
88+
"description" => menu.description,
89+
"menuItems" => get_menu_items(menu)
8690
}
8791
|> recursively_order_menu_items()
8892
end
@@ -212,7 +216,7 @@ defmodule Sanbase.Menus do
212216
case Map.get(params, :position) do
213217
nil ->
214218
# If `position` is not specified, add it at the end by getting the last position + 1
215-
{:ok, get_next_position(params.parent_id)}
219+
get_next_position(params.parent_id)
216220

217221
position when is_integer(position) ->
218222
# If `position` is specified, bump all the positions bigger than it by 1 in
@@ -256,17 +260,32 @@ defmodule Sanbase.Menus do
256260
get_menu_item_for_update(menu_item_id, user_id)
257261
end)
258262
|> Ecto.Multi.run(
259-
:maybe_update_items_positions,
263+
:get_new_params_after_position_adjustment,
260264
fn _repo, %{get_menu_item_for_update: menu_item} ->
261265
case Map.get(params, :position) do
262266
nil ->
263-
{:ok, nil}
267+
parent_id = Map.get(params, :parent_id)
268+
269+
# We cannot change the parent_id without also specifying the position
270+
case is_nil(parent_id) or parent_id == menu_item.parent_id do
271+
true ->
272+
{:ok, params}
273+
274+
false ->
275+
{:error,
276+
"If the parent_id for a menu item is updated, the position in the new menu must also be specified"}
277+
end
264278

265279
position when is_integer(position) ->
266280
# If `position` is specified, bump all the positions bigger than it by 1 in
267281
# order to avoid having multiple items with the same position.
268-
{:ok, {_, nil}} = inc_all_positions_after(menu_item.parent_id, position)
269-
{:ok, position}
282+
#
283+
# If the menu gets its parent_id also changed , the bumping
284+
# must happen in the new parent menu, not in the old one.
285+
parent_id_after_update = Map.get(params, :parent_id, menu_item.parent_id)
286+
287+
{:ok, {_, nil}} = inc_all_positions_after(parent_id_after_update, position)
288+
{:ok, params}
270289
end
271290
end
272291
)
@@ -307,7 +326,7 @@ defmodule Sanbase.Menus do
307326
query = Menu.get_for_update(menu_id, user_id)
308327

309328
case Repo.one(query) do
310-
nil -> {:error, "Menu item does not exist"}
329+
nil -> {:error, "Menu with id #{menu_id} not found or it is owned by another user"}
311330
menu -> {:ok, menu}
312331
end
313332
end
@@ -316,13 +335,18 @@ defmodule Sanbase.Menus do
316335
query = MenuItem.get_for_update(menu_item_id, user_id)
317336

318337
case Repo.one(query) do
319-
nil -> {:error, "Menu item does not exist"}
320-
menu -> {:ok, menu}
338+
nil ->
339+
{:error,
340+
"Menu item with id #{menu_item_id} not found or it is part of a menu owned by another user"}
341+
342+
menu ->
343+
{:ok, menu}
321344
end
322345
end
323346

324347
defp get_next_position(menu_id) do
325348
query = MenuItem.get_next_position(menu_id)
349+
326350
{:ok, Repo.one(query)}
327351
end
328352

@@ -341,15 +365,15 @@ defmodule Sanbase.Menus do
341365
do: {:error, error}
342366

343367
# Helpers for transforming a menu struct to a simple map
344-
defp recursively_order_menu_items(%{menu_items: menu_items} = map) do
368+
defp recursively_order_menu_items(%{"menuItems" => menu_items} = map) do
345369
sorted_menu_items =
346-
Enum.sort_by(menu_items, & &1.position, :asc)
370+
Enum.sort_by(menu_items, & &1["position"], :asc)
347371
|> Enum.map(fn
348-
%{menu_items: [_ | _]} = elem -> recursively_order_menu_items(elem)
372+
%{"menuItems" => [_ | _]} = elem -> recursively_order_menu_items(elem)
349373
x -> x
350374
end)
351375

352-
%{map | menu_items: sorted_menu_items}
376+
%{map | "menuItems" => sorted_menu_items}
353377
end
354378

355379
defp recursively_order_menu_items(data), do: data
@@ -359,17 +383,38 @@ defmodule Sanbase.Menus do
359383
defp get_menu_items(%Menu{menu_items: list}) when is_list(list) do
360384
list
361385
|> Enum.map(fn
362-
%{id: menu_item_id, query: %{id: _} = map, position: position} ->
363-
Map.take(map, [:id, :name, :description])
364-
|> Map.merge(%{type: :query, position: position, menu_item_id: menu_item_id})
386+
%{id: menu_item_id, query: %{} = map, position: position} ->
387+
%{
388+
"name" => map.name,
389+
"description" => map.description,
390+
"entityType" => :query,
391+
"entityId" => map.id,
392+
"position" => position,
393+
"menuItemId" => menu_item_id
394+
}
365395

366-
%{id: menu_item_id, dashboard: %{id: _} = map, position: position} ->
367-
Map.take(map, [:id, :name, :description])
368-
|> Map.merge(%{type: :dashboard, position: position, menu_item_id: menu_item_id})
396+
%{id: menu_item_id, dashboard: %{} = map, position: position} ->
397+
Map.take(map, [:name, :description])
398+
399+
%{
400+
"name" => map.name,
401+
"description" => map.description,
402+
"entityType" => :dashboard,
403+
"entityId" => map.id,
404+
"position" => position,
405+
"menuItemId" => menu_item_id
406+
}
369407

370-
%{id: menu_item_id, menu: %{id: _} = map, position: position} ->
408+
%{id: menu_item_id, menu: %{} = map, position: position} ->
371409
menu_to_simple_map(map)
372-
|> Map.merge(%{type: :menu, position: position, menu_item_id: menu_item_id})
410+
|> Map.merge(%{
411+
"name" => map.name,
412+
"description" => map.description,
413+
"entityType" => :menu,
414+
"entityId" => map.id,
415+
"position" => position,
416+
"menuItemId" => menu_item_id
417+
})
373418
end)
374419
end
375420
end

lib/sanbase/run_examples.ex

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -800,7 +800,13 @@ defmodule Sanbase.RunExamples do
800800

801801
{:ok, _} =
802802
Sanbase.Menus.create_menu_item(
803-
%{parent_id: menu.id, dashboard_id: dashboard.id, position: 2},
803+
%{parent_id: menu.id, dashboard_id: dashboard.id, position: 1},
804+
user.id
805+
)
806+
807+
{:ok, _} =
808+
Sanbase.Menus.create_menu_item(
809+
%{parent_id: menu.id, dashboard_id: dashboard.id},
804810
user.id
805811
)
806812

lib/sanbase_web/graphql/resolvers/menu_resolver.ex.ex

Lines changed: 22 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,14 @@ defmodule SanbaseWeb.Graphql.Resolvers.MenuResolver do
22
alias Sanbase.Menus
33

44
# Menu CRUD
5+
defp maybe_transform_menu({:ok, menu}) do
6+
transformed_menu = Menus.menu_to_simple_map(menu)
7+
{:ok, transformed_menu}
8+
end
9+
10+
defp maybe_transform_menu({:error, reason}) do
11+
{:error, reason}
12+
end
513

614
def get_menu(
715
_root,
@@ -10,31 +18,23 @@ defmodule SanbaseWeb.Graphql.Resolvers.MenuResolver do
1018
) do
1119
querying_user_id = get_in(resolution.context.auth, [:current_user, Access.key(:id)])
1220

13-
case Menus.get_menu(menu_id, querying_user_id) do
14-
{:ok, menu} -> {:ok, Menus.menu_to_simple_map(menu)}
15-
{:error, reason} -> {:error, reason}
16-
end
21+
Menus.get_menu(menu_id, querying_user_id)
22+
|> maybe_transform_menu()
1723
end
1824

1925
def create_menu(_root, %{name: _} = param, %{context: %{auth: %{current_user: current_user}}}) do
20-
case Menus.create_menu(param, current_user.id) do
21-
{:ok, menu} -> {:ok, Menus.menu_to_simple_map(menu)}
22-
{:error, reason} -> {:error, reason}
23-
end
26+
Menus.create_menu(param, current_user.id)
27+
|> maybe_transform_menu()
2428
end
2529

2630
def update_menu(_root, %{id: id} = params, %{context: %{auth: %{current_user: current_user}}}) do
27-
case Menus.update_menu(id, params, current_user.id) do
28-
{:ok, menu} -> {:ok, Menus.menu_to_simple_map(menu)}
29-
{:error, reason} -> {:error, reason}
30-
end
31+
Menus.update_menu(id, params, current_user.id)
32+
|> maybe_transform_menu()
3133
end
3234

3335
def delete_menu(_root, %{id: id}, %{context: %{auth: %{current_user: current_user}}}) do
34-
case Menus.delete_menu(id, current_user.id) do
35-
{:ok, menu} -> {:ok, Menus.menu_to_simple_map(menu)}
36-
{:error, reason} -> {:error, reason}
37-
end
36+
Menus.delete_menu(id, current_user.id)
37+
|> maybe_transform_menu()
3838
end
3939

4040
# MenuItem C~R~UD
@@ -43,27 +43,21 @@ defmodule SanbaseWeb.Graphql.Resolvers.MenuResolver do
4343
context: %{auth: %{current_user: current_user}}
4444
}) do
4545
with {:ok, params} <- create_menu_item_params(args) do
46-
case Menus.create_menu_item(params, current_user.id) do
47-
{:ok, menu} -> {:ok, Menus.menu_to_simple_map(menu)}
48-
{:error, reason} -> {:error, reason}
49-
end
46+
Menus.create_menu_item(params, current_user.id)
47+
|> maybe_transform_menu()
5048
end
5149
end
5250

5351
def update_menu_item(_root, %{id: id} = params, %{
5452
context: %{auth: %{current_user: current_user}}
5553
}) do
56-
case Menus.update_menu_item(id, params, current_user.id) do
57-
{:ok, menu} -> {:ok, Menus.menu_to_simple_map(menu)}
58-
{:error, reason} -> {:error, reason}
59-
end
54+
Menus.update_menu_item(id, params, current_user.id)
55+
|> maybe_transform_menu()
6056
end
6157

6258
def delete_menu_item(_root, %{id: id}, %{context: %{auth: %{current_user: current_user}}}) do
63-
case Menus.delete_menu_item(id, current_user.id) do
64-
{:ok, menu} -> {:ok, Menus.menu_to_simple_map(menu)}
65-
{:error, reason} -> {:error, reason}
66-
end
59+
Menus.delete_menu_item(id, current_user.id)
60+
|> maybe_transform_menu()
6761
end
6862

6963
# Private functions

priv/repo/structure.sql

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9095,6 +9095,5 @@ INSERT INTO public."schema_migrations" (version) VALUES (20231012130814);
90959095
INSERT INTO public."schema_migrations" (version) VALUES (20231019111320);
90969096
INSERT INTO public."schema_migrations" (version) VALUES (20231023123140);
90979097
INSERT INTO public."schema_migrations" (version) VALUES (20231026084628);
9098-
INSERT INTO public."schema_migrations" (version) VALUES (20231030143950);
90999098
INSERT INTO public."schema_migrations" (version) VALUES (20231101104145);
91009099
INSERT INTO public."schema_migrations" (version) VALUES (20231110093800);

0 commit comments

Comments
 (0)