From b0d39398a083452b332ee7f2c9376e84576db0ed Mon Sep 17 00:00:00 2001 From: Jonas Martinez <36544012+jonas-martinez@users.noreply.github.com> Date: Thu, 4 Apr 2024 14:09:33 +0200 Subject: [PATCH 1/3] fix: App Adapter Guard (#565) --- apps/lenra_web/lib/lenra_web/app_adapter.ex | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/apps/lenra_web/lib/lenra_web/app_adapter.ex b/apps/lenra_web/lib/lenra_web/app_adapter.ex index 3a3a9e63..e31917e9 100644 --- a/apps/lenra_web/lib/lenra_web/app_adapter.ex +++ b/apps/lenra_web/lib/lenra_web/app_adapter.ex @@ -126,7 +126,9 @@ defmodule LenraWeb.AppAdapter do }), do: true - def authorize(:join_app, user, app) when not is_nil(user) do + def authorize(:join_app, nil, _app), do: false + + def authorize(:join_app, user, app) do case Apps.fetch_user_env_access( environment_id: app.main_env.environment.id, user_id: user.id From fc2e3fda50708ed3a7e95b6319dabd2a9f38bc80 Mon Sep 17 00:00:00 2001 From: Jonas Martinez <36544012+jonas-martinez@users.noreply.github.com> Date: Fri, 12 Apr 2024 11:39:55 +0200 Subject: [PATCH 2/3] fix: Hydra OAuth Token invalid when server is reset (#568) --- .../controllers/user_auth_controller.ex | 23 +++++++++++++------ 1 file changed, 16 insertions(+), 7 deletions(-) diff --git a/apps/identity_web/lib/identity_web/controllers/user_auth_controller.ex b/apps/identity_web/lib/identity_web/controllers/user_auth_controller.ex index 2eba9ec1..223724c9 100644 --- a/apps/identity_web/lib/identity_web/controllers/user_auth_controller.ex +++ b/apps/identity_web/lib/identity_web/controllers/user_auth_controller.ex @@ -147,20 +147,29 @@ defmodule IdentityWeb.UserAuthController do case Accounts.get_user(response.body["subject"]) do # In case the user from the token is nil, the token is invalid - nil -> {:error, {:invalid_token, "The token is invalid.", 403}} - res -> redirect_next_step(conn, res, login_challenge, true) + nil -> + conn + |> redirect_to_register(login_challenge) + + res -> + redirect_next_step(conn, res, login_challenge, true) end else # client = response.body["client"] conn - |> delete_session(:user_id) - |> delete_session(:remember) - |> delete_session(:email) - |> put_session(:login_challenge, login_challenge) - |> redirect(to: Routes.user_auth_path(conn, :register_page)) + |> redirect_to_register(login_challenge) end end + defp redirect_to_register(conn, login_challenge) do + conn + |> delete_session(:user_id) + |> delete_session(:remember) + |> delete_session(:email) + |> put_session(:login_challenge, login_challenge) + |> redirect(to: Routes.user_auth_path(conn, :register_page)) + end + def new(conn, _params), do: redirect(conn, to: Routes.user_auth_path(conn, :register_page)) From 72d8104571e9d519d75a7f4a03ab9cdff51e16c5 Mon Sep 17 00:00:00 2001 From: Thomas DA ROCHA Date: Wed, 29 May 2024 10:19:10 +0200 Subject: [PATCH 3/3] feat: Handle view components in JSON view response (#567) --- .../lib/environment/manifest_handler.ex | 2 +- .../lib/session/route_server.ex | 4 +- .../lib/session/ui_builders/json_builder.ex | 102 +++-- .../lib/session/ui_builders/lenra_builder.ex | 127 +----- .../session/ui_builders/ui_builder_adapter.ex | 127 ++++++ libs/application_runner/mix.exs | 1 + libs/application_runner/mix.lock | 10 +- .../session/ui_builders/json_builder_test.exs | 397 ++++++++++++++++++ .../ui_builders/lenra_builder_test.exs | 343 +++++++++++++++ .../ui_builders/ui_builder_adapter_test.exs | 167 ++++++++ .../test/support/ui_builder_adapter.ex | 27 ++ 11 files changed, 1167 insertions(+), 140 deletions(-) create mode 100644 libs/application_runner/test/session/ui_builders/json_builder_test.exs create mode 100644 libs/application_runner/test/session/ui_builders/lenra_builder_test.exs create mode 100644 libs/application_runner/test/session/ui_builders/ui_builder_adapter_test.exs create mode 100644 libs/application_runner/test/support/ui_builder_adapter.ex diff --git a/libs/application_runner/lib/environment/manifest_handler.ex b/libs/application_runner/lib/environment/manifest_handler.ex index bcee76ad..f7e93bf4 100644 --- a/libs/application_runner/lib/environment/manifest_handler.ex +++ b/libs/application_runner/lib/environment/manifest_handler.ex @@ -44,7 +44,7 @@ defmodule ApplicationRunner.Environment.ManifestHandler do {:ok, GenServer.call(get_full_name(env_id), {:get_routes, exposer})} end - defp get_routes(env_id, exposer) do + defp get_routes(_env_id, exposer) do {:error, "Exposer #{exposer} not supported"} end diff --git a/libs/application_runner/lib/session/route_server.ex b/libs/application_runner/lib/session/route_server.ex index 9ddf10ca..4b1013a6 100644 --- a/libs/application_runner/lib/session/route_server.ex +++ b/libs/application_runner/lib/session/route_server.ex @@ -212,7 +212,7 @@ defmodule ApplicationRunner.Session.RouteServer do query_params |> Map.merge(%{ "me" => mongo_user_id, - "roles" => session_metadata.user_id + "roles" => session_metadata.roles }) query_transformed = Parser.replace_params(query, params) @@ -221,7 +221,7 @@ defmodule ApplicationRunner.Session.RouteServer do context |> Map.merge(%{ "me" => mongo_user_id, - "roles" => session_metadata.user_id, + "roles" => session_metadata.roles, "pathParams" => query_params["route"] }) |> project_map(context_projection) diff --git a/libs/application_runner/lib/session/ui_builders/json_builder.ex b/libs/application_runner/lib/session/ui_builders/json_builder.ex index c025b618..6c41ffaa 100644 --- a/libs/application_runner/lib/session/ui_builders/json_builder.ex +++ b/libs/application_runner/lib/session/ui_builders/json_builder.ex @@ -5,54 +5,102 @@ defmodule ApplicationRunner.Session.UiBuilders.JsonBuilder do @behaviour ApplicationRunner.Session.UiBuilders.UiBuilderAdapter alias ApplicationRunner.Environment - alias ApplicationRunner.Session.RouteServer + alias ApplicationRunner.Session.UiBuilders.UiBuilderAdapter require Logger @type view :: map() @type component :: map() - @impl ApplicationRunner.Session.UiBuilders.UiBuilderAdapter + @impl true def get_routes(env_id, roles) do Environment.ManifestHandler.get_routes(env_id, "json", roles) end - @impl ApplicationRunner.Session.UiBuilders.UiBuilderAdapter + @impl true def build_ui(session_metadata, view_uid) do - Logger.debug("#{__MODULE__} build_ui with session_metadata: #{inspect(session_metadata)}") + UiBuilderAdapter.build_ui(__MODULE__, session_metadata, view_uid) + end + + @impl true + def build_components( + session_metadata, + %{"_type" => comp_type} = component, + ui_context, + view_uid + ) do + Logger.debug("#{__MODULE__} build_components with component: #{inspect(component)}") + + # TODO: validate component ? + case comp_type do + "view" -> + UiBuilderAdapter.handle_view(__MODULE__, session_metadata, component, ui_context, view_uid) + + "listener" -> + UiBuilderAdapter.handle_listener(__MODULE__, session_metadata, component, ui_context, view_uid) - with {:ok, json} <- RouteServer.fetch_view(session_metadata, view_uid) do - build_listeners(session_metadata, json) + _ -> + Logger.warn("Unknown component type for JSON view: #{comp_type}") + {:ok, component, ui_context} end end - def build_listeners(session_metadata, view) do - {:ok, do_build_listeners(session_metadata, view)} - rescue - err -> {:error, err} - end + def build_components( + session_metadata, + component, + ui_context, + view_uid + ) + when is_map(component) do + {new_context, new_component} = + Enum.reduce( + component, + {ui_context, %{}}, + fn {k, v}, {context, acc} -> + case build_components(session_metadata, v, context, view_uid) do + {:ok, new_sub_component, new_ui_context} -> + {new_ui_context, Map.put(acc, k, new_sub_component)} + + _ -> + {context, Map.put(acc, k, v)} + end + end + ) - defp do_build_listeners(session_metadata, list) when is_list(list) do - Enum.map(list, &do_build_listeners(session_metadata, &1)) + {:ok, new_component, new_context} end - defp do_build_listeners(session_metadata, %{"_type" => "listener"} = listener) do - case RouteServer.build_listener(session_metadata, listener) do - {:ok, built_listener} -> - built_listener + def build_components( + session_metadata, + component, + ui_context, + view_uid + ) + when is_list(component) do + {new_context, new_component} = + Enum.reduce( + component, + {ui_context, []}, + fn v, {context, acc} -> + case build_components(session_metadata, v, context, view_uid) do + {:ok, new_sub_component, new_ui_context} -> + {new_ui_context, [new_sub_component | acc]} - err -> - raise err - end - end + _ -> + {context, [v | acc]} + end + end + ) - defp do_build_listeners(session_metadata, map) when is_map(map) do - map - |> Enum.map(fn {k, v} -> {k, do_build_listeners(session_metadata, v)} end) - |> Map.new() + {:ok, Enum.reverse(new_component), new_context} end - defp do_build_listeners(_session_metadata, e) do - e + def build_components( + _session_metadata, + component, + ui_context, + _view_uid + ) do + {:ok, component, ui_context} end end diff --git a/libs/application_runner/lib/session/ui_builders/lenra_builder.ex b/libs/application_runner/lib/session/ui_builders/lenra_builder.ex index 689b074e..40310cf3 100644 --- a/libs/application_runner/lib/session/ui_builders/lenra_builder.ex +++ b/libs/application_runner/lib/session/ui_builders/lenra_builder.ex @@ -15,78 +15,33 @@ defmodule ApplicationRunner.Session.UiBuilders.LenraBuilder do @type view :: map() @type component :: map() + @impl true def get_routes(env_id, roles) do Environment.ManifestHandler.get_routes(env_id, "lenra", roles) end + @impl true def build_ui(session_metadata, view_uid) do - Logger.debug("#{__MODULE__} build_ui with session_metadata: #{inspect(session_metadata)}") - - with {:ok, ui_context} <- get_and_build_view(session_metadata, Ui.Context.new(), view_uid) do - {:ok, - transform_ui(%{ - "rootView" => view_id(view_uid), - "views" => ui_context.views_map - })} - end - end - - defp transform_ui(%{"rootView" => root_views, "views" => views}) do - transform(%{"root" => Map.fetch!(views, root_views)}, views) - end - - defp transform(%{"type" => "view", "id" => id}, views) do - transform(Map.fetch!(views, id), views) - end - - defp transform(view, views) when is_map(view) do - Enum.map(view, fn - {k, v} -> {k, transform(v, views)} - end) - |> Map.new() - end - - defp transform(view, views) when is_list(view) do - Enum.map(view, &transform(&1, views)) - end - - defp transform(view, _views) do - view - end - - @spec get_and_build_view(Session.Metadata.t(), Ui.Context.t(), ViewUid.t()) :: - {:ok, Ui.Context.t()} | {:error, UiBuilderAdapter.common_error()} - defp get_and_build_view( - %Session.Metadata{} = session_metadata, - %Ui.Context{} = ui_context, - %ViewUid{} = view_uid - ) do - with {:ok, view} <- RouteServer.fetch_view(session_metadata, view_uid), - {:ok, component, new_app_context} <- - build_component(session_metadata, view, ui_context, view_uid) do - str_view_id = view_id(view_uid) - {:ok, put_in(new_app_context.views_map[str_view_id], component)} + with {:ok, ui} <- UiBuilderAdapter.build_ui(__MODULE__, session_metadata, view_uid) do + {:ok, %{"root" => ui}} end end - # Build a component. - # If the component type is "view" this is considered a view and will be handled like one. - # Everything else will be handled as a simple component. - @spec build_component(Session.Metadata.t(), view(), Ui.Context.t(), ViewUid.t()) :: - {:ok, component(), Ui.Context.t()} | {:error, UiBuilderAdapter.common_error()} - defp build_component( - session_metadata, - %{"_type" => comp_type} = component, - ui_context, - view_uid - ) do + # Build the view result components. + @impl true + def build_components( + session_metadata, + %{"_type" => comp_type} = component, + ui_context, + view_uid + ) do Logger.debug("#{__MODULE__} build_component with component: #{inspect(component)}") with schema_path <- JsonSchemata.get_component_path(comp_type), {:ok, validation_data} <- validate_with_error(schema_path, component, view_uid) do case comp_type do "view" -> - handle_view(session_metadata, component, ui_context, view_uid) + UiBuilderAdapter.handle_view(__MODULE__, session_metadata, component, ui_context, view_uid) _ -> handle_component( @@ -100,12 +55,12 @@ defmodule ApplicationRunner.Session.UiBuilders.LenraBuilder do end end - defp build_component( - _session_metadata, - component, - _ui_context, - view_uid - ) do + def build_components( + _session_metadata, + component, + _ui_context, + view_uid + ) do ApplicationRunner.Errors.BusinessError.components_malformated_tuple(%{ view: view_uid.name, at: view_uid.prefix_path, @@ -113,42 +68,6 @@ defmodule ApplicationRunner.Session.UiBuilders.LenraBuilder do }) end - # Build a view means : - # - getting the name and props, coll and query of the view - # - create the ID of the view with name/data/props - # - Create a new viewContext corresponding to the view - # - Recursively get_and_build_view. - @spec handle_view(Session.Metadata.t(), view(), Ui.Context.t(), ViewUid.t()) :: - {:ok, component(), Ui.Context.t()} | {:error, UiBuilderAdapter.common_error()} - defp handle_view(session_metadata, component, ui_context, view_uid) do - name = Map.get(component, "name") - props = Map.get(component, "props") - find = Map.get(component, "find", %{}) - context_projection = Map.get(component, "context") - - {coll, query, projection} = RouteServer.extract_find(component, find) - - with {:ok, new_view_uid} <- - RouteServer.create_view_uid( - session_metadata, - name, - %{coll: coll, query: query, projection: projection}, - %{}, - props, - view_uid.context, - context_projection, - view_uid.prefix_path - ), - {:ok, new_app_context} <- - get_and_build_view(session_metadata, ui_context, new_view_uid) do - { - :ok, - %{"type" => "view", "id" => view_id(new_view_uid), "name" => name}, - new_app_context - } - end - end - # Build a components means to : # - Recursively build all children (list of child) properties # - Recursively build all single child properties @@ -287,7 +206,7 @@ defmodule ApplicationRunner.Session.UiBuilders.LenraBuilder do ui_context, view_uid ) do - case build_component( + case build_components( session_metadata, child_comp, ui_context, @@ -401,7 +320,7 @@ defmodule ApplicationRunner.Session.UiBuilders.LenraBuilder do |> Parallel.map(fn {child, index} -> children_path = "#{prefix_path}/#{index}" - build_component( + build_components( session_metadata, child, ui_context, @@ -459,8 +378,4 @@ defmodule ApplicationRunner.Session.UiBuilders.LenraBuilder do end ) end - - defp view_id(%ViewUid{} = view_uid) do - Crypto.hash({view_uid.name, view_uid.coll, view_uid.query_parsed, view_uid.props}) - end end diff --git a/libs/application_runner/lib/session/ui_builders/ui_builder_adapter.ex b/libs/application_runner/lib/session/ui_builders/ui_builder_adapter.ex index cfab47ee..0e4b50ce 100644 --- a/libs/application_runner/lib/session/ui_builders/ui_builder_adapter.ex +++ b/libs/application_runner/lib/session/ui_builders/ui_builder_adapter.ex @@ -5,12 +5,139 @@ defmodule ApplicationRunner.Session.UiBuilders.UiBuilderAdapter do alias ApplicationRunner.Environment.ViewUid alias ApplicationRunner.Session + alias ApplicationRunner.Session.RouteServer + alias ApplicationRunner.Session.UiBuilders.UiBuilderAdapter + alias ApplicationRunner.Ui alias LenraCommon.Errors + require Logger + + @type view :: map() + @type component :: map() + @type common_error :: Errors.BusinessError.t() | Errors.TechnicalError.t() @callback build_ui(Session.Metadata.t(), ViewUid.t()) :: {:ok, map()} | {:error, common_error()} @callback get_routes(number(), list(binary())) :: list(binary()) + + @callback build_components(Session.Metadata.t(), map(), Ui.Context.t(), ViewUid.t()) :: + {:ok, map(), Ui.Context.t()} | {:error, common_error()} + + @spec build_ui( + module(), + ApplicationRunner.Session.Metadata.t(), + ApplicationRunner.Environment.ViewUid.t() + ) :: + {:error, + %{ + __exception__: true, + __struct__: LenraCommon.Errors.BusinessError | LenraCommon.Errors.TechnicalError, + message: binary(), + metadata: any(), + reason: atom(), + status_code: integer() + }} + | {:ok, any()} + def build_ui(adapter, session_metadata, view_uid) do + Logger.debug("#{__MODULE__} build_ui with session_metadata: #{inspect(session_metadata)}") + + with {:ok, ui_context} <- get_and_build_view(adapter, session_metadata, Ui.Context.new(), view_uid) do + {:ok, transform(Map.fetch!(ui_context.views_map, view_id(view_uid)), ui_context.views_map)} + end + end + + defp transform(%{"_type" => "view", "id" => id}, views) do + transform(Map.fetch!(views, id), views) + end + + defp transform(view, views) when is_map(view) do + Enum.map(view, fn + {k, v} -> {k, transform(v, views)} + end) + |> Map.new() + end + + defp transform(view, views) when is_list(view) do + Enum.map(view, &transform(&1, views)) + end + + defp transform(view, _views) do + view + end + + @spec get_and_build_view(module(), Session.Metadata.t(), Ui.Context.t(), ViewUid.t()) :: + {:ok, Ui.Context.t()} | {:error, UiBuilderAdapter.common_error()} + defp get_and_build_view( + adapter, + %Session.Metadata{} = session_metadata, + %Ui.Context{} = ui_context, + %ViewUid{} = view_uid + ) do + with {:ok, view} <- RouteServer.fetch_view(session_metadata, view_uid), + {:ok, component, new_app_context} <- + adapter.build_components(session_metadata, view, ui_context, view_uid) do + str_view_id = view_id(view_uid) + {:ok, put_in(new_app_context.views_map[str_view_id], component)} + end + end + + # Build a view means : + # - getting the name and props, coll and query of the view + # - create the ID of the view with name/data/props + # - Create a new viewContext corresponding to the view + # - Recursively get_and_build_view. + @spec handle_view(module(), Session.Metadata.t(), view(), Ui.Context.t(), ViewUid.t()) :: + {:ok, component(), Ui.Context.t()} | {:error, UiBuilderAdapter.common_error()} + def handle_view(adapter, session_metadata, component, ui_context, view_uid) do + name = Map.get(component, "name") + props = Map.get(component, "props") + find = Map.get(component, "find", %{}) + context_projection = Map.get(component, "context") + + {coll, query, projection} = RouteServer.extract_find(component, find) + + with {:ok, new_view_uid} <- + RouteServer.create_view_uid( + session_metadata, + name, + %{coll: coll, query: query, projection: projection}, + %{}, + props, + session_metadata.context, + context_projection, + view_uid.prefix_path + ), + {:ok, new_app_context} <- + get_and_build_view(adapter, session_metadata, ui_context, new_view_uid) do + { + :ok, + %{"_type" => "view", "id" => view_id(new_view_uid), "name" => name}, + new_app_context + } + end + end + + # Build a view means : + # - getting the name and props, coll and query of the view + # - create the ID of the view with name/data/props + # - Create a new viewContext corresponding to the view + # - Recursively get_and_build_view. + @spec handle_listener(module(), Session.Metadata.t(), view(), Ui.Context.t(), ViewUid.t()) :: + {:ok, component(), Ui.Context.t()} | {:error, UiBuilderAdapter.common_error()} + def handle_listener(_adapter, session_metadata, component, ui_context, _view_uid) do + with {:ok, listener} <- + RouteServer.build_listener(session_metadata, component) do + { + :ok, + listener, + ui_context + } + end + end + + defp view_id(%ViewUid{} = view_uid) do + Crypto.hash({view_uid.name, view_uid.coll, view_uid.query_parsed, view_uid.props}) + end end diff --git a/libs/application_runner/mix.exs b/libs/application_runner/mix.exs index 83d7aa5e..e6926ad8 100644 --- a/libs/application_runner/mix.exs +++ b/libs/application_runner/mix.exs @@ -34,6 +34,7 @@ defmodule ApplicationRunner.MixProject do [ {:credo, "~> 1.6.7", only: [:dev, :test], runtime: false}, {:dialyxir, "~> 1.2", only: [:dev, :test], runtime: false}, + {:mock, "~> 0.3.8", only: [:test], runtime: false}, {:ex_component_schema, git: "https://github.com/lenra-io/ex_component_schema", ref: "v1.0.0-beta.6"}, {:jason, "~> 1.4"}, {:json_diff, "~> 0.1.3"}, diff --git a/libs/application_runner/mix.lock b/libs/application_runner/mix.lock index f14bab0c..fc51dd8e 100644 --- a/libs/application_runner/mix.lock +++ b/libs/application_runner/mix.lock @@ -1,11 +1,11 @@ %{ "bunt": {:hex, :bunt, "0.2.1", "e2d4792f7bc0ced7583ab54922808919518d0e57ee162901a16a1b6664ef3b14", [:mix], [], "hexpm", "a330bfb4245239787b15005e66ae6845c9cd524a288f0d141c148b02603777a5"}, "bypass": {:hex, :bypass, "2.1.0", "909782781bf8e20ee86a9cabde36b259d44af8b9f38756173e8f5e2e1fabb9b1", [:mix], [{:plug, "~> 1.7", [hex: :plug, repo: "hexpm", optional: false]}, {:plug_cowboy, "~> 2.0", [hex: :plug_cowboy, repo: "hexpm", optional: false]}, {:ranch, "~> 1.3", [hex: :ranch, repo: "hexpm", optional: false]}], "hexpm", "d9b5df8fa5b7a6efa08384e9bbecfe4ce61c77d28a4282f79e02f1ef78d96b80"}, - "castore": {:hex, :castore, "1.0.5", "9eeebb394cc9a0f3ae56b813459f990abb0a3dedee1be6b27fdb50301930502f", [:mix], [], "hexpm", "8d7c597c3e4a64c395980882d4bca3cebb8d74197c590dc272cfd3b6a6310578"}, + "castore": {:hex, :castore, "1.0.6", "ffc42f110ebfdafab0ea159cd43d31365fa0af0ce4a02ecebf1707ae619ee727", [:mix], [], "hexpm", "374c6e7ca752296be3d6780a6d5b922854ffcc74123da90f2f328996b962d33a"}, "connection": {:hex, :connection, "1.1.0", "ff2a49c4b75b6fb3e674bfc5536451607270aac754ffd1bdfe175abe4a6d7a68", [:mix], [], "hexpm", "722c1eb0a418fbe91ba7bd59a47e28008a189d47e37e0e7bb85585a016b2869c"}, - "cowboy": {:hex, :cowboy, "2.10.0", "ff9ffeff91dae4ae270dd975642997afe2a1179d94b1887863e43f681a203e26", [:make, :rebar3], [{:cowlib, "2.12.1", [hex: :cowlib, repo: "hexpm", optional: false]}, {:ranch, "1.8.0", [hex: :ranch, repo: "hexpm", optional: false]}], "hexpm", "3afdccb7183cc6f143cb14d3cf51fa00e53db9ec80cdcd525482f5e99bc41d6b"}, + "cowboy": {:hex, :cowboy, "2.12.0", "f276d521a1ff88b2b9b4c54d0e753da6c66dd7be6c9fca3d9418b561828a3731", [:make, :rebar3], [{:cowlib, "2.13.0", [hex: :cowlib, repo: "hexpm", optional: false]}, {:ranch, "1.8.0", [hex: :ranch, repo: "hexpm", optional: false]}], "hexpm", "8a7abe6d183372ceb21caa2709bec928ab2b72e18a3911aa1771639bef82651e"}, "cowboy_telemetry": {:hex, :cowboy_telemetry, "0.4.0", "f239f68b588efa7707abce16a84d0d2acf3a0f50571f8bb7f56a15865aae820c", [:rebar3], [{:cowboy, "~> 2.7", [hex: :cowboy, repo: "hexpm", optional: false]}, {:telemetry, "~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "7d98bac1ee4565d31b62d59f8823dfd8356a169e7fcbb83831b8a5397404c9de"}, - "cowlib": {:hex, :cowlib, "2.12.1", "a9fa9a625f1d2025fe6b462cb865881329b5caff8f1854d1cbc9f9533f00e1e1", [:make, :rebar3], [], "hexpm", "163b73f6367a7341b33c794c4e88e7dbfe6498ac42dcd69ef44c5bc5507c8db0"}, + "cowlib": {:hex, :cowlib, "2.13.0", "db8f7505d8332d98ef50a3ef34b34c1afddec7506e4ee4dd4a3a266285d282ca", [:make, :rebar3], [], "hexpm", "e1e1284dc3fc030a64b1ad0d8382ae7e99da46c3246b815318a4b848873800a4"}, "credo": {:hex, :credo, "1.6.7", "323f5734350fd23a456f2688b9430e7d517afb313fbd38671b8a4449798a7854", [:mix], [{:bunt, "~> 0.2.1", [hex: :bunt, repo: "hexpm", optional: false]}, {:file_system, "~> 0.2.8", [hex: :file_system, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "41e110bfb007f7eda7f897c10bf019ceab9a0b269ce79f015d54b0dcf4fc7dd3"}, "crontab": {:hex, :crontab, "1.1.13", "3bad04f050b9f7f1c237809e42223999c150656a6b2afbbfef597d56df2144c5", [:mix], [{:ecto, "~> 1.0 or ~> 2.0 or ~> 3.0", [hex: :ecto, repo: "hexpm", optional: true]}], "hexpm", "d67441bec989640e3afb94e123f45a2bc42d76e02988c9613885dc3d01cf7085"}, "db_connection": {:hex, :db_connection, "2.6.0", "77d835c472b5b67fc4f29556dee74bf511bbafecdcaf98c27d27fa5918152086", [:mix], [{:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "c2f992d15725e721ec7fbc1189d4ecdb8afef76648c746a8e1cad35e3b8a35f3"}, @@ -26,8 +26,10 @@ "json_diff": {:hex, :json_diff, "0.1.3", "c80d5ca5416e785867e765e906e9a91b7efc35bfd505af276654d108f4995736", [:mix], [], "hexpm", "a5332e8293e7e9f384d34ea44645d7961334db73739165178fd4a7728d06f7d1"}, "lenra_common": {:hex, :lenra_common, "2.9.0", "78c941b4878d10fed9ec3bbf44437221c662d5556a441a9a832175069414796d", [:mix], [{:jason, "~> 1.4", [hex: :jason, repo: "hexpm", optional: false]}, {:phoenix, "~> 1.6.15", [hex: :phoenix, repo: "hexpm", optional: false]}], "hexpm", "ce907706adc0fbb72b373c1f1f868205d1ed8ffa7e9d7b4c0db9c25320b3dcc0"}, "libring": {:hex, :libring, "1.6.0", "d5dca4bcb1765f862ab59f175b403e356dec493f565670e0bacc4b35e109ce0d", [:mix], [], "hexpm", "5e91ece396af4bce99953d49ee0b02f698cd38326d93cd068361038167484319"}, + "meck": {:hex, :meck, "0.9.2", "85ccbab053f1db86c7ca240e9fc718170ee5bda03810a6292b5306bf31bae5f5", [:rebar3], [], "hexpm", "81344f561357dc40a8344afa53767c32669153355b626ea9fcbc8da6b3045826"}, "mime": {:hex, :mime, "2.0.5", "dc34c8efd439abe6ae0343edbb8556f4d63f178594894720607772a041b04b02", [:mix], [], "hexpm", "da0d64a365c45bc9935cc5c8a7fc5e49a0e0f9932a761c55d6c52b142780a05c"}, "mint": {:hex, :mint, "1.5.1", "8db5239e56738552d85af398798c80648db0e90f343c8469f6c6d8898944fb6f", [:mix], [{:castore, "~> 0.1.0 or ~> 1.0", [hex: :castore, repo: "hexpm", optional: true]}, {:hpax, "~> 0.1.1", [hex: :hpax, repo: "hexpm", optional: false]}], "hexpm", "4a63e1e76a7c3956abd2c72f370a0d0aecddc3976dea5c27eccbecfa5e7d5b1e"}, + "mock": {:hex, :mock, "0.3.8", "7046a306b71db2488ef54395eeb74df0a7f335a7caca4a3d3875d1fc81c884dd", [:mix], [{:meck, "~> 0.9.2", [hex: :meck, repo: "hexpm", optional: false]}], "hexpm", "7fa82364c97617d79bb7d15571193fc0c4fe5afd0c932cef09426b3ee6fe2022"}, "mongodb_driver": {:hex, :mongodb_driver, "1.2.1", "f626705cbf4fc64048d66814da8c09bc4c432fa267f8cff1b079b21aa280373f", [:mix], [{:db_connection, "~> 2.6", [hex: :db_connection, repo: "hexpm", optional: false]}, {:decimal, "~> 2.1.1", [hex: :decimal, repo: "hexpm", optional: false]}, {:telemetry, "~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "35a51a1d1531a1a842af98634ad7bbeec87f46b1b8b55c776d3169cfd5b51baf"}, "neotomex": {:hex, :neotomex, "0.1.7", "64f76513653aa87ea7abdde0fd600e56955d838020a13d88f2bf334c88ac3e7a", [:mix], [], "hexpm", "4b87b8f614d1cd89dc8ba80ba0e559bedb3ebf6f6d74cd774fcfdd215e861445"}, "nimble_options": {:hex, :nimble_options, "1.0.2", "92098a74df0072ff37d0c12ace58574d26880e522c22801437151a159392270e", [:mix], [], "hexpm", "fd12a8db2021036ce12a309f26f564ec367373265b53e25403f0ee697380f1b8"}, @@ -37,7 +39,7 @@ "phoenix_template": {:hex, :phoenix_template, "1.0.4", "e2092c132f3b5e5b2d49c96695342eb36d0ed514c5b252a77048d5969330d639", [:mix], [{:phoenix_html, "~> 2.14.2 or ~> 3.0 or ~> 4.0", [hex: :phoenix_html, repo: "hexpm", optional: true]}], "hexpm", "2c0c81f0e5c6753faf5cca2f229c9709919aba34fab866d3bc05060c9c444206"}, "phoenix_view": {:hex, :phoenix_view, "2.0.3", "4d32c4817fce933693741deeb99ef1392619f942633dde834a5163124813aad3", [:mix], [{:phoenix_html, "~> 2.14.2 or ~> 3.0 or ~> 4.0", [hex: :phoenix_html, repo: "hexpm", optional: true]}, {:phoenix_template, "~> 1.0", [hex: :phoenix_template, repo: "hexpm", optional: false]}], "hexpm", "cd34049af41be2c627df99cd4eaa71fc52a328c0c3d8e7d4aa28f880c30e7f64"}, "plug": {:hex, :plug, "1.15.3", "712976f504418f6dff0a3e554c40d705a9bcf89a7ccef92fc6a5ef8f16a30a97", [:mix], [{:mime, "~> 1.0 or ~> 2.0", [hex: :mime, repo: "hexpm", optional: false]}, {:plug_crypto, "~> 1.1.1 or ~> 1.2 or ~> 2.0", [hex: :plug_crypto, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4.3 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "cc4365a3c010a56af402e0809208873d113e9c38c401cabd88027ef4f5c01fd2"}, - "plug_cowboy": {:hex, :plug_cowboy, "2.7.0", "3ae9369c60641084363b08fe90267cbdd316df57e3557ea522114b30b63256ea", [:mix], [{:cowboy, "~> 2.7.0 or ~> 2.8.0 or ~> 2.9.0 or ~> 2.10.0", [hex: :cowboy, repo: "hexpm", optional: false]}, {:cowboy_telemetry, "~> 0.3", [hex: :cowboy_telemetry, repo: "hexpm", optional: false]}, {:plug, "~> 1.14", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm", "d85444fb8aa1f2fc62eabe83bbe387d81510d773886774ebdcb429b3da3c1a4a"}, + "plug_cowboy": {:hex, :plug_cowboy, "2.7.1", "87677ffe3b765bc96a89be7960f81703223fe2e21efa42c125fcd0127dd9d6b2", [:mix], [{:cowboy, "~> 2.7", [hex: :cowboy, repo: "hexpm", optional: false]}, {:cowboy_telemetry, "~> 0.3", [hex: :cowboy_telemetry, repo: "hexpm", optional: false]}, {:plug, "~> 1.14", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm", "02dbd5f9ab571b864ae39418db7811618506256f6d13b4a45037e5fe78dc5de3"}, "plug_crypto": {:hex, :plug_crypto, "1.2.5", "918772575e48e81e455818229bf719d4ab4181fcbf7f85b68a35620f78d89ced", [:mix], [], "hexpm", "26549a1d6345e2172eb1c233866756ae44a9609bd33ee6f99147ab3fd87fd842"}, "poison": {:hex, :poison, "5.0.0", "d2b54589ab4157bbb82ec2050757779bfed724463a544b6e20d79855a9e43b24", [:mix], [{:decimal, "~> 2.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm", "11dc6117c501b80c62a7594f941d043982a1bd05a1184280c0d9166eb4d8d3fc"}, "postgrex": {:hex, :postgrex, "0.16.5", "fcc4035cc90e23933c5d69a9cd686e329469446ef7abba2cf70f08e2c4b69810", [:mix], [{:connection, "~> 1.1", [hex: :connection, repo: "hexpm", optional: false]}, {:db_connection, "~> 2.1", [hex: :db_connection, repo: "hexpm", optional: false]}, {:decimal, "~> 1.5 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:table, "~> 0.1.0", [hex: :table, repo: "hexpm", optional: true]}], "hexpm", "edead639dc6e882618c01d8fc891214c481ab9a3788dfe38dd5e37fd1d5fb2e8"}, diff --git a/libs/application_runner/test/session/ui_builders/json_builder_test.exs b/libs/application_runner/test/session/ui_builders/json_builder_test.exs new file mode 100644 index 00000000..21241509 --- /dev/null +++ b/libs/application_runner/test/session/ui_builders/json_builder_test.exs @@ -0,0 +1,397 @@ +defmodule ApplicationRunner.Session.UiBuilders.JsonBuilderTest do + use ExUnit.Case + doctest ApplicationRunner.Session.UiBuilders.JsonBuilder + + alias ApplicationRunner.Environment.ViewUid + alias ApplicationRunner.Session.Metadata + alias ApplicationRunner.Session.RouteServer + alias ApplicationRunner.Session.UiBuilders.JsonBuilder + alias ApplicationRunner.Session.UiBuilders.UiBuilderAdapter + alias ApplicationRunner.Ui.Context + + import Mock + + @session_uuid Ecto.UUID.generate() + + setup_with_mocks([ + {UiBuilderAdapter, [:passthrough], + [ + handle_view: fn _module, _session_metadata, _component, ui_context, _view_uid -> + {:ok, %{"_type" => "view", "id" => "viewID"}, ui_context} + end + ]}, + {RouteServer, [:passthrough], + [ + build_listener: fn _session_metadata, _component -> + {:ok, %{"_type" => "listener", "code" => "listenerCode"}} + end + ]} + ]) do + :ok + end + + describe "build_components/4" do + test "string" do + # Prepare + session_metadata = %Metadata{ + env_id: 1, + session_id: @session_uuid, + user_id: nil, + roles: ["guest"], + function_name: "test", + context: %{} + } + + ui_context = Context.new() + + view_uid = %ViewUid{ + name: "test_view", + props: nil, + query_parsed: nil, + query_transformed: nil, + coll: nil, + context: nil, + prefix_path: "", + projection: %{} + } + + # Act + result = + JsonBuilder.build_components( + session_metadata, + "coucou", + ui_context, + view_uid + ) + + # Assert + assert {:ok, component, _ui_context} = result + + # Add your assertions here + assert component == "coucou" + + assert_not_called(UiBuilderAdapter.handle_view(:_, :_, :_, :_, :_)) + assert_not_called(RouteServer.build_listener(:_, :_)) + end + + test "array" do + # Prepare + session_metadata = %Metadata{ + env_id: 1, + session_id: @session_uuid, + user_id: nil, + roles: ["guest"], + function_name: "test", + context: %{} + } + + ui_context = Context.new() + + view_uid = %ViewUid{ + name: "test_view", + props: nil, + query_parsed: nil, + query_transformed: nil, + coll: nil, + context: nil, + prefix_path: "", + projection: %{} + } + + # Act + result = + JsonBuilder.build_components( + session_metadata, + ["coucou"], + ui_context, + view_uid + ) + + # Assert + assert {:ok, component, _ui_context} = result + + # Add your assertions here + assert component == ["coucou"] + + assert_not_called(UiBuilderAdapter.handle_view(:_, :_, :_, :_, :_)) + assert_not_called(RouteServer.build_listener(:_, :_)) + end + + test "basic object" do + # Prepare + session_metadata = %Metadata{ + env_id: 1, + session_id: @session_uuid, + user_id: nil, + roles: ["guest"], + function_name: "test", + context: %{} + } + + ui_context = Context.new() + + view_uid = %ViewUid{ + name: "test_view", + props: nil, + query_parsed: nil, + query_transformed: nil, + coll: nil, + context: nil, + prefix_path: "", + projection: %{} + } + + # Act + result = + JsonBuilder.build_components( + session_metadata, + %{"key" => "value"}, + ui_context, + view_uid + ) + + # Assert + assert {:ok, component, _ui_context} = result + + # Add your assertions here + assert component == %{"key" => "value"} + + assert_not_called(UiBuilderAdapter.handle_view(:_, :_, :_, :_, :_)) + assert_not_called(RouteServer.build_listener(:_, :_)) + end + + test "simple view component" do + # Prepare + session_metadata = %Metadata{ + env_id: 1, + session_id: @session_uuid, + user_id: nil, + roles: ["guest"], + function_name: "test", + context: %{} + } + + ui_context = Context.new() + + view_uid = %ViewUid{ + name: "test_view", + props: nil, + query_parsed: nil, + query_transformed: nil, + coll: nil, + context: nil, + prefix_path: "", + projection: %{} + } + + # Act + result = + JsonBuilder.build_components( + session_metadata, + %{"_type" => "view", "name" => "my_view"}, + ui_context, + view_uid + ) + + # Assert + assert {:ok, component, _ui_context} = result + + # Add your assertions here + assert component == %{"_type" => "view", "id" => "viewID"} + + assert_called(UiBuilderAdapter.handle_view(:_, :_, :_, :_, :_)) + assert_not_called(RouteServer.build_listener(:_, :_)) + end + + test "complete view component" do + # Prepare + session_metadata = %Metadata{ + env_id: 1, + session_id: @session_uuid, + user_id: nil, + roles: ["guest"], + function_name: "test", + context: %{} + } + + ui_context = Context.new() + + view_uid = %ViewUid{ + name: "test_view", + props: nil, + query_parsed: nil, + query_transformed: nil, + coll: nil, + context: nil, + prefix_path: "", + projection: %{} + } + + # Act + result = + JsonBuilder.build_components( + session_metadata, + %{ + "_type" => "view", + "name" => "my_view", + "props" => %{"key" => "value"}, + "find" => %{"coll" => "my_collection", "query" => %{"key" => "value"}}, + "context" => %{"me" => true} + }, + ui_context, + view_uid + ) + + # Assert + assert {:ok, component, _ui_context} = result + + # Add your assertions here + assert component == %{"_type" => "view", "id" => "viewID"} + + assert_called(UiBuilderAdapter.handle_view(:_, :_, :_, :_, :_)) + assert_not_called(RouteServer.build_listener(:_, :_)) + end + + test "simple listener component" do + # Prepare + session_metadata = %Metadata{ + env_id: 1, + session_id: @session_uuid, + user_id: nil, + roles: ["guest"], + function_name: "test", + context: %{} + } + + ui_context = Context.new() + + view_uid = %ViewUid{ + name: "test_view", + props: nil, + query_parsed: nil, + query_transformed: nil, + coll: nil, + context: nil, + prefix_path: "", + projection: %{} + } + + # Act + result = + JsonBuilder.build_components( + session_metadata, + %{ + "_type" => "listener", + "name" => "my_listener" + }, + ui_context, + view_uid + ) + + # Assert + assert {:ok, component, _ui_context} = result + + # Add your assertions here + assert component == %{"_type" => "listener", "code" => "listenerCode"} + + assert_not_called(UiBuilderAdapter.handle_view(:_, :_, :_, :_, :_)) + assert_called(RouteServer.build_listener(:_, :_)) + end + + test "complete listener component" do + # Prepare + session_metadata = %Metadata{ + env_id: 1, + session_id: @session_uuid, + user_id: nil, + roles: ["guest"], + function_name: "test", + context: %{} + } + + ui_context = Context.new() + + view_uid = %ViewUid{ + name: "test_view", + props: nil, + query_parsed: nil, + query_transformed: nil, + coll: nil, + context: nil, + prefix_path: "", + projection: %{} + } + + # Act + result = + JsonBuilder.build_components( + session_metadata, + %{ + "_type" => "listener", + "name" => "my_listener", + "props" => %{"key" => "value"} + }, + ui_context, + view_uid + ) + + # Assert + assert {:ok, component, _ui_context} = result + + # Add your assertions here + assert component == %{"_type" => "listener", "code" => "listenerCode"} + + assert_not_called(UiBuilderAdapter.handle_view(:_, :_, :_, :_, :_)) + assert_called(RouteServer.build_listener(:_, :_)) + end + + test "complexe object with components" do + # Prepare + session_metadata = %Metadata{ + env_id: 1, + session_id: @session_uuid, + user_id: nil, + roles: ["guest"], + function_name: "test", + context: %{} + } + + ui_context = Context.new() + + view_uid = %ViewUid{ + name: "test_view", + props: nil, + query_parsed: nil, + query_transformed: nil, + coll: nil, + context: nil, + prefix_path: "", + projection: %{} + } + + # Act + result = + JsonBuilder.build_components( + session_metadata, + %{ + "elements" => ["coucou", %{"_type" => "view", "name" => "my_view"}], + "on_click" => %{"_type" => "listener", "name" => "my_listener"} + }, + ui_context, + view_uid + ) + + # Assert + assert {:ok, component, _ui_context} = result + + # Add your assertions here + assert component == %{ + "elements" => ["coucou", %{"_type" => "view", "id" => "viewID"}], + "on_click" => %{"_type" => "listener", "code" => "listenerCode"} + } + + assert_called(UiBuilderAdapter.handle_view(:_, :_, :_, :_, :_)) + assert_called(RouteServer.build_listener(:_, :_)) + end + end +end diff --git a/libs/application_runner/test/session/ui_builders/lenra_builder_test.exs b/libs/application_runner/test/session/ui_builders/lenra_builder_test.exs new file mode 100644 index 00000000..eeb9471d --- /dev/null +++ b/libs/application_runner/test/session/ui_builders/lenra_builder_test.exs @@ -0,0 +1,343 @@ +defmodule ApplicationRunner.Session.UiBuilders.LenraBuilderTest do + use ExUnit.Case + doctest ApplicationRunner.Session.UiBuilders.LenraBuilder + + alias ApplicationRunner.Environment.ViewUid + alias ApplicationRunner.Session.Metadata + alias ApplicationRunner.Session.RouteServer + alias ApplicationRunner.Session.UiBuilders.LenraBuilder + alias ApplicationRunner.Session.UiBuilders.UiBuilderAdapter + alias ApplicationRunner.Ui.Context + + import Mock + + @session_uuid Ecto.UUID.generate() + + setup_with_mocks([ + {UiBuilderAdapter, [:passthrough], + [ + handle_view: fn _module, _session_metadata, _component, ui_context, _view_uid -> + {:ok, %{"_type" => "view", "id" => "viewID"}, ui_context} + end + ]}, + {RouteServer, [:passthrough], + [ + build_listener: fn _session_metadata, _component -> + {:ok, %{"_type" => "listener", "code" => "listenerCode"}} + end + ]} + ]) do + :ok + end + + describe "build_components/4" do + test "text" do + # Prepare + session_metadata = %Metadata{ + env_id: 1, + session_id: @session_uuid, + user_id: nil, + roles: ["guest"], + function_name: "test", + context: %{} + } + + ui_context = Context.new() + + view_uid = %ViewUid{ + name: "test_view", + props: nil, + query_parsed: nil, + query_transformed: nil, + coll: nil, + context: nil, + prefix_path: "", + projection: %{} + } + + # Act + result = + LenraBuilder.build_components( + session_metadata, + %{"_type" => "text", "value" => "coucou"}, + ui_context, + view_uid + ) + + # Assert + assert {:ok, component, _ui_context} = result + + # Add your assertions here + assert component == %{"_type" => "text", "value" => "coucou"} + + assert_not_called(UiBuilderAdapter.handle_view(:_, :_, :_, :_, :_)) + assert_not_called(RouteServer.build_listener(:_, :_)) + end + + test "simple view component" do + # Prepare + session_metadata = %Metadata{ + env_id: 1, + session_id: @session_uuid, + user_id: nil, + roles: ["guest"], + function_name: "test", + context: %{} + } + + ui_context = Context.new() + + view_uid = %ViewUid{ + name: "test_view", + props: nil, + query_parsed: nil, + query_transformed: nil, + coll: nil, + context: nil, + prefix_path: "", + projection: %{} + } + + # Act + result = + LenraBuilder.build_components( + session_metadata, + %{"_type" => "view", "name" => "my_view"}, + ui_context, + view_uid + ) + + # Assert + assert {:ok, component, _ui_context} = result + + # Add your assertions here + assert component == %{"_type" => "view", "id" => "viewID"} + + assert_called(UiBuilderAdapter.handle_view(:_, :_, :_, :_, :_)) + assert_not_called(RouteServer.build_listener(:_, :_)) + end + + test "complete view component" do + # Prepare + session_metadata = %Metadata{ + env_id: 1, + session_id: @session_uuid, + user_id: nil, + roles: ["guest"], + function_name: "test", + context: %{} + } + + ui_context = Context.new() + + view_uid = %ViewUid{ + name: "test_view", + props: nil, + query_parsed: nil, + query_transformed: nil, + coll: nil, + context: nil, + prefix_path: "", + projection: %{} + } + + # Act + result = + LenraBuilder.build_components( + session_metadata, + %{ + "_type" => "view", + "name" => "my_view", + "props" => %{"key" => "value"}, + "find" => %{"coll" => "my_collection", "query" => %{"key" => "value"}}, + "context" => %{"me" => true} + }, + ui_context, + view_uid + ) + + # Assert + assert {:ok, component, _ui_context} = result + + # Add your assertions here + assert component == %{"_type" => "view", "id" => "viewID"} + + assert_called(UiBuilderAdapter.handle_view(:_, :_, :_, :_, :_)) + assert_not_called(RouteServer.build_listener(:_, :_)) + end + + test "simple listener component" do + # Prepare + session_metadata = %Metadata{ + env_id: 1, + session_id: @session_uuid, + user_id: nil, + roles: ["guest"], + function_name: "test", + context: %{} + } + + ui_context = Context.new() + + view_uid = %ViewUid{ + name: "test_view", + props: nil, + query_parsed: nil, + query_transformed: nil, + coll: nil, + context: nil, + prefix_path: "", + projection: %{} + } + + # Act + result = + LenraBuilder.build_components( + session_metadata, + %{ + "_type" => "listener", + "name" => "my_listener" + }, + ui_context, + view_uid + ) + + # Assert + assert {:error, + %LenraCommon.Errors.BusinessError{ + message: "Invalid component type\n\n", + reason: :build_errors, + metadata: nil, + status_code: 400 + }} = result + + assert_not_called(UiBuilderAdapter.handle_view(:_, :_, :_, :_, :_)) + assert_not_called(RouteServer.build_listener(:_, :_)) + end + + test "complete listener component" do + # Prepare + session_metadata = %Metadata{ + env_id: 1, + session_id: @session_uuid, + user_id: nil, + roles: ["guest"], + function_name: "test", + context: %{} + } + + ui_context = Context.new() + + view_uid = %ViewUid{ + name: "test_view", + props: nil, + query_parsed: nil, + query_transformed: nil, + coll: nil, + context: nil, + prefix_path: "", + projection: %{} + } + + # Act + result = + LenraBuilder.build_components( + session_metadata, + %{ + "_type" => "listener", + "name" => "my_listener", + "props" => %{"key" => "value"} + }, + ui_context, + view_uid + ) + + # Assert + assert {:error, + %LenraCommon.Errors.BusinessError{ + message: "Invalid component type\n\n", + reason: :build_errors, + metadata: nil, + status_code: 400 + }} = result + + assert_not_called(UiBuilderAdapter.handle_view(:_, :_, :_, :_, :_)) + assert_not_called(RouteServer.build_listener(:_, :_)) + end + + test "complexe UI" do + # Prepare + session_metadata = %Metadata{ + env_id: 1, + session_id: @session_uuid, + user_id: nil, + roles: ["guest"], + function_name: "test", + context: %{} + } + + ui_context = Context.new() + + view_uid = %ViewUid{ + name: "test_view", + props: nil, + query_parsed: nil, + query_transformed: nil, + coll: nil, + context: nil, + prefix_path: "", + projection: %{} + } + + # Act + result = + LenraBuilder.build_components( + session_metadata, + %{ + "_type" => "container", + "child" => %{ + "_type" => "flex", + "children" => [ + %{ + "_type" => "view", + "name" => "my_view" + }, + %{ + "_type" => "button", + "text" => "Click me", + "onPressed" => %{ + "_type" => "listener", + "name" => "my_listener" + } + } + ] + } + }, + ui_context, + view_uid + ) + + # Assert + assert {:ok, component, _ui_context} = result + + # Add your assertions here + assert component == %{ + "_type" => "container", + "child" => %{ + "_type" => "flex", + "children" => [ + %{"_type" => "view", "id" => "viewID"}, + %{ + "_type" => "button", + "onPressed" => %{"_type" => "listener", "code" => "listenerCode"}, + "text" => "Click me" + } + ] + } + } + + assert_called(UiBuilderAdapter.handle_view(:_, :_, :_, :_, :_)) + assert_called(RouteServer.build_listener(:_, :_)) + end + end +end diff --git a/libs/application_runner/test/session/ui_builders/ui_builder_adapter_test.exs b/libs/application_runner/test/session/ui_builders/ui_builder_adapter_test.exs new file mode 100644 index 00000000..cc51c1a7 --- /dev/null +++ b/libs/application_runner/test/session/ui_builders/ui_builder_adapter_test.exs @@ -0,0 +1,167 @@ +defmodule ApplicationRunner.Session.UiBuilders.UiBuilderAdapterTest do + use ExUnit.Case + doctest ApplicationRunner.Session.UiBuilders.UiBuilderAdapter + + alias ApplicationRunner.Environment.ViewUid + alias ApplicationRunner.Session.Metadata + alias ApplicationRunner.Session.RouteServer + alias ApplicationRunner.Session.UiBuilders.UiBuilderAdapter + alias ApplicationRunner.Ui.Context + + import Mock + + @session_uuid Ecto.UUID.generate() + + describe "build_ui/3" do + test "basic component" do + with_mock RouteServer, + fetch_view: fn _session_metadata, _view_uid -> {:ok, %{"count" => 1}} end do + # Prepare + session_metadata = %Metadata{ + env_id: 1, + session_id: @session_uuid, + user_id: nil, + roles: ["guest"], + function_name: "test", + context: %{} + } + + view_uid = %ViewUid{ + name: "test_view", + props: nil, + query_parsed: nil, + query_transformed: nil, + coll: nil, + context: nil, + prefix_path: "", + projection: %{} + } + + adapter = ApplicationRunner.FakeUiBuilderAdapter + + # Act + result = UiBuilderAdapter.build_ui(adapter, session_metadata, view_uid) + + # Assert + assert {:ok, ui} = result + + # Add your assertions here + assert ui == %{"count" => 1} + + assert called(RouteServer.fetch_view(:_, :_)) + end + end + end + + describe "handle_view/5" do + test "basic view" do + view_result = %{"count" => 1} + + new_view_uid = %ViewUid{ + name: "test_view_new", + props: nil, + query_parsed: nil, + query_transformed: nil, + coll: nil, + context: nil, + prefix_path: "", + projection: %{} + } + + with_mock RouteServer, + fetch_view: fn _session_metadata, _view_uid -> {:ok, view_result} end, + extract_find: fn _component, _find -> {nil, nil, nil} end, + create_view_uid: fn _session_metadata, + _name, + _find, + _query_params, + _props, + _context, + _context_projection, + _prefix_path -> + {:ok, new_view_uid} + end do + # Prepare + session_metadata = %Metadata{ + env_id: 1, + session_id: @session_uuid, + user_id: nil, + roles: ["guest"], + function_name: "test", + context: %{} + } + + view_uid = %ViewUid{ + name: "test_view", + props: nil, + query_parsed: nil, + query_transformed: nil, + coll: nil, + context: nil, + prefix_path: "", + projection: %{} + } + + ui_context = Context.new() + component = %{"_type" => "view", "name" => "test_view_new"} + adapter = ApplicationRunner.FakeUiBuilderAdapter + + # Act + result = UiBuilderAdapter.handle_view(adapter, session_metadata, component, ui_context, view_uid) + + # Assert + assert {:ok, view, updated_context} = result + + # Add your assertions here + assert %{"_type" => "view", "name" => "test_view_new", "id" => new_view_uid} = view + + assert updated_context.views_map[new_view_uid] == view_result + + assert called(RouteServer.fetch_view(:_, :_)) + end + end + end + + describe "handle_listener/5" do + test "basic listener" do + with_mock RouteServer, + build_listener: fn _session_metadata, _component -> {:ok, %{"_type" => "listener", "code" => "the_code"}} end do + # Prepare + session_metadata = %Metadata{ + env_id: 1, + session_id: @session_uuid, + user_id: nil, + roles: ["guest"], + function_name: "test", + context: %{} + } + + view_uid = %ViewUid{ + name: "test_view", + props: nil, + query_parsed: nil, + query_transformed: nil, + coll: nil, + context: nil, + prefix_path: "", + projection: %{} + } + + ui_context = %Context{} + component = %{"_type" => "listener", "name" => "test_listener"} + adapter = ApplicationRunner.FakeUiBuilderAdapter + + # Act + result = UiBuilderAdapter.handle_listener(adapter, session_metadata, component, ui_context, view_uid) + + # Assert + assert {:ok, listener, updated_context} = result + + # Add your assertions here + assert listener == %{"_type" => "listener", "code" => "the_code"} + + assert called(RouteServer.build_listener(:_, component)) + end + end + end +end diff --git a/libs/application_runner/test/support/ui_builder_adapter.ex b/libs/application_runner/test/support/ui_builder_adapter.ex new file mode 100644 index 00000000..5b044180 --- /dev/null +++ b/libs/application_runner/test/support/ui_builder_adapter.ex @@ -0,0 +1,27 @@ +defmodule ApplicationRunner.FakeUiBuilderAdapter do + @moduledoc """ + This adapter emulate a builder adapter. + """ + alias ApplicationRunner.Errors.TechnicalError + @behaviour ApplicationRunner.Session.UiBuilders.UiBuilderAdapter + + @impl true + def get_routes(_env_id, _roles) do + [] + end + + @impl true + def build_ui(_session_metadata, _view_uid) do + {:ok, %{}} + end + + @impl true + def build_components( + _session_metadata, + component, + ui_context, + _view_uid + ) do + {:ok, component, ui_context} + end +end