From f1aaf7e7029ac1c9f91ecf4216a51388580195ad Mon Sep 17 00:00:00 2001 From: "Elias W. BA" Date: Wed, 23 Mar 2022 14:33:30 +0000 Subject: [PATCH 1/5] Generate credentials model --- lib/lightning/credentials.ex | 104 ++++++++++++++++++ lib/lightning/credentials/credential.ex | 20 ++++ .../20220323142451_create_credentials.exs | 13 +++ test/lightning/credentials_test.exs | 61 ++++++++++ test/support/fixtures/credentials_fixtures.ex | 21 ++++ 5 files changed, 219 insertions(+) create mode 100644 lib/lightning/credentials.ex create mode 100644 lib/lightning/credentials/credential.ex create mode 100644 priv/repo/migrations/20220323142451_create_credentials.exs create mode 100644 test/lightning/credentials_test.exs create mode 100644 test/support/fixtures/credentials_fixtures.ex diff --git a/lib/lightning/credentials.ex b/lib/lightning/credentials.ex new file mode 100644 index 0000000000..de42f3bb0f --- /dev/null +++ b/lib/lightning/credentials.ex @@ -0,0 +1,104 @@ +defmodule Lightning.Credentials do + @moduledoc """ + The Credentials context. + """ + + import Ecto.Query, warn: false + alias Lightning.Repo + + alias Lightning.Credentials.Credential + + @doc """ + Returns the list of credentials. + + ## Examples + + iex> list_credentials() + [%Credential{}, ...] + + """ + def list_credentials do + Repo.all(Credential) + end + + @doc """ + Gets a single credential. + + Raises `Ecto.NoResultsError` if the Credential does not exist. + + ## Examples + + iex> get_credential!(123) + %Credential{} + + iex> get_credential!(456) + ** (Ecto.NoResultsError) + + """ + def get_credential!(id), do: Repo.get!(Credential, id) + + @doc """ + Creates a credential. + + ## Examples + + iex> create_credential(%{field: value}) + {:ok, %Credential{}} + + iex> create_credential(%{field: bad_value}) + {:error, %Ecto.Changeset{}} + + """ + def create_credential(attrs \\ %{}) do + %Credential{} + |> Credential.changeset(attrs) + |> Repo.insert() + end + + @doc """ + Updates a credential. + + ## Examples + + iex> update_credential(credential, %{field: new_value}) + {:ok, %Credential{}} + + iex> update_credential(credential, %{field: bad_value}) + {:error, %Ecto.Changeset{}} + + """ + def update_credential(%Credential{} = credential, attrs) do + credential + |> Credential.changeset(attrs) + |> Repo.update() + end + + @doc """ + Deletes a credential. + + ## Examples + + iex> delete_credential(credential) + {:ok, %Credential{}} + + iex> delete_credential(credential) + {:error, %Ecto.Changeset{}} + + """ + def delete_credential(%Credential{} = credential) do + Repo.delete(credential) + end + + @doc """ + Returns an `%Ecto.Changeset{}` for tracking credential changes. + + ## Examples + + iex> change_credential(credential) + %Ecto.Changeset{data: %Credential{}} + + """ + def change_credential(%Credential{} = credential, attrs \\ %{}) do + Credential.changeset(credential, attrs) + end +end diff --git a/lib/lightning/credentials/credential.ex b/lib/lightning/credentials/credential.ex new file mode 100644 index 0000000000..e4d5e22c4c --- /dev/null +++ b/lib/lightning/credentials/credential.ex @@ -0,0 +1,20 @@ +defmodule Lightning.Credentials.Credential do + use Ecto.Schema + import Ecto.Changeset + + @primary_key {:id, :binary_id, autogenerate: true} + @foreign_key_type :binary_id + schema "credentials" do + field :body, :map + field :name, :string + + timestamps() + end + + @doc false + def changeset(credential, attrs) do + credential + |> cast(attrs, [:name, :body]) + |> validate_required([:name, :body]) + end +end diff --git a/priv/repo/migrations/20220323142451_create_credentials.exs b/priv/repo/migrations/20220323142451_create_credentials.exs new file mode 100644 index 0000000000..f0325b1c21 --- /dev/null +++ b/priv/repo/migrations/20220323142451_create_credentials.exs @@ -0,0 +1,13 @@ +defmodule Lightning.Repo.Migrations.CreateCredentials do + use Ecto.Migration + + def change do + create table(:credentials, primary_key: false) do + add :id, :binary_id, primary_key: true + add :name, :string + add :body, :map, default: %{} + + timestamps() + end + end +end diff --git a/test/lightning/credentials_test.exs b/test/lightning/credentials_test.exs new file mode 100644 index 0000000000..1e62783e1e --- /dev/null +++ b/test/lightning/credentials_test.exs @@ -0,0 +1,61 @@ +defmodule Lightning.CredentialsTest do + use Lightning.DataCase + + alias Lightning.Credentials + + describe "credentials" do + alias Lightning.Credentials.Credential + + import Lightning.CredentialsFixtures + + @invalid_attrs %{body: nil, name: nil} + + test "list_credentials/0 returns all credentials" do + credential = credential_fixture() + assert Credentials.list_credentials() == [credential] + end + + test "get_credential!/1 returns the credential with given id" do + credential = credential_fixture() + assert Credentials.get_credential!(credential.id) == credential + end + + test "create_credential/1 with valid data creates a credential" do + valid_attrs = %{body: %{}, name: "some name"} + + assert {:ok, %Credential{} = credential} = Credentials.create_credential(valid_attrs) + assert credential.body == %{} + assert credential.name == "some name" + end + + test "create_credential/1 with invalid data returns error changeset" do + assert {:error, %Ecto.Changeset{}} = Credentials.create_credential(@invalid_attrs) + end + + test "update_credential/2 with valid data updates the credential" do + credential = credential_fixture() + update_attrs = %{body: %{}, name: "some updated name"} + + assert {:ok, %Credential{} = credential} = Credentials.update_credential(credential, update_attrs) + assert credential.body == %{} + assert credential.name == "some updated name" + end + + test "update_credential/2 with invalid data returns error changeset" do + credential = credential_fixture() + assert {:error, %Ecto.Changeset{}} = Credentials.update_credential(credential, @invalid_attrs) + assert credential == Credentials.get_credential!(credential.id) + end + + test "delete_credential/1 deletes the credential" do + credential = credential_fixture() + assert {:ok, %Credential{}} = Credentials.delete_credential(credential) + assert_raise Ecto.NoResultsError, fn -> Credentials.get_credential!(credential.id) end + end + + test "change_credential/1 returns a credential changeset" do + credential = credential_fixture() + assert %Ecto.Changeset{} = Credentials.change_credential(credential) + end + end +end diff --git a/test/support/fixtures/credentials_fixtures.ex b/test/support/fixtures/credentials_fixtures.ex new file mode 100644 index 0000000000..85cd9ef076 --- /dev/null +++ b/test/support/fixtures/credentials_fixtures.ex @@ -0,0 +1,21 @@ +defmodule Lightning.CredentialsFixtures do + @moduledoc """ + This module defines test helpers for creating + entities via the `Lightning.Credentials` context. + """ + + @doc """ + Generate a credential. + """ + def credential_fixture(attrs \\ %{}) do + {:ok, credential} = + attrs + |> Enum.into(%{ + body: %{}, + name: "some name" + }) + |> Lightning.Credentials.create_credential() + + credential + end +end From 03ca4ff7b1d09a0912511c31925f4a0965970f31 Mon Sep 17 00:00:00 2001 From: "Elias W. BA" Date: Wed, 23 Mar 2022 14:47:46 +0000 Subject: [PATCH 2/5] Add credentials routing to liveview --- lib/lightning_web/router.ex | 64 ++++++++++++++++++++----------------- 1 file changed, 35 insertions(+), 29 deletions(-) diff --git a/lib/lightning_web/router.ex b/lib/lightning_web/router.ex index d7203309b9..d927afcd6e 100644 --- a/lib/lightning_web/router.ex +++ b/lib/lightning_web/router.ex @@ -3,50 +3,56 @@ defmodule LightningWeb.Router do alias JobLive pipeline :browser do - plug :accepts, ["html"] - plug :fetch_session - plug :fetch_live_flash - plug :put_root_layout, {LightningWeb.LayoutView, :root} - plug :protect_from_forgery - plug :put_secure_browser_headers, %{"content-security-policy" => "default-src 'self'"} + plug(:accepts, ["html"]) + plug(:fetch_session) + plug(:fetch_live_flash) + plug(:put_root_layout, {LightningWeb.LayoutView, :root}) + plug(:protect_from_forgery) + plug(:put_secure_browser_headers, %{"content-security-policy" => "default-src 'self'"}) end pipeline :api do - plug :accepts, ["json"] + plug(:accepts, ["json"]) end scope "/", LightningWeb do - pipe_through :browser + pipe_through(:browser) - live "/", DashboardLive.Index, :index + live("/", DashboardLive.Index, :index) - live "/jobs", JobLive.Index, :index - live "/jobs/new", JobLive.Index, :new + live("/jobs", JobLive.Index, :index) + live("/jobs/new", JobLive.Index, :new) # live "/jobs/:id/edit", JobLive.Index, :edit - live "/jobs/:id", JobLive.Show, :show - live "/jobs/:id/edit", JobLive.Edit, :edit + live("/jobs/:id", JobLive.Show, :show) + live("/jobs/:id/edit", JobLive.Edit, :edit) # live "/jobs/:id/show/edit", JobLive.Show, :edit - live "/dataclips", DataclipLive.Index, :index - live "/dataclips/new", DataclipLive.Index, :new - live "/dataclips/:id/edit", DataclipLive.Index, :edit + live("/credentials", CredentialLive.Index, :index) + live("/credentials/new", CredentialLive.Index, :new) - live "/dataclips/:id", DataclipLive.Show, :show - live "/dataclips/:id/show/edit", DataclipLive.Show, :edit + live("/credentials/:id", CredentialLive.Show, :show) + live("/credentials/:id/edit", CredentialLive.Edit, :edit) - live "/runs", RunLive.Index, :index - live "/runs/new", RunLive.Index, :new - live "/runs/:id/edit", RunLive.Index, :edit + live("/dataclips", DataclipLive.Index, :index) + live("/dataclips/new", DataclipLive.Index, :new) + live("/dataclips/:id/edit", DataclipLive.Index, :edit) - live "/runs/:id", RunLive.Show, :show - live "/runs/:id/show/edit", RunLive.Show, :edit + live("/dataclips/:id", DataclipLive.Show, :show) + live("/dataclips/:id/show/edit", DataclipLive.Show, :edit) + + live("/runs", RunLive.Index, :index) + live("/runs/new", RunLive.Index, :new) + live("/runs/:id/edit", RunLive.Index, :edit) + + live("/runs/:id", RunLive.Show, :show) + live("/runs/:id/show/edit", RunLive.Show, :edit) end scope "/i", LightningWeb do - pipe_through :api + pipe_through(:api) - post "/*path", WebhooksController, :create + post("/*path", WebhooksController, :create) end # Other scopes may use custom stacks. @@ -65,9 +71,9 @@ defmodule LightningWeb.Router do import Phoenix.LiveDashboard.Router scope "/" do - pipe_through :browser + pipe_through(:browser) - live_dashboard "/dashboard", metrics: LightningWeb.Telemetry + live_dashboard("/dashboard", metrics: LightningWeb.Telemetry) end end @@ -77,9 +83,9 @@ defmodule LightningWeb.Router do # node running the Phoenix server. if Mix.env() == :dev do scope "/dev" do - pipe_through :browser + pipe_through(:browser) - forward "/mailbox", Plug.Swoosh.MailboxPreview + forward("/mailbox", Plug.Swoosh.MailboxPreview) end end end From 681f08aa68e502c66a5a020580ee67600bb43510 Mon Sep 17 00:00:00 2001 From: "Elias W. BA" Date: Wed, 23 Mar 2022 19:54:39 +0000 Subject: [PATCH 3/5] Credential body validation --- lib/lightning/credentials.ex | 10 +++- lib/lightning/credentials/credential.ex | 4 ++ lib/lightning/helpers.ex | 35 +++++++++++ lib/lightning/invocation.ex | 32 ++-------- .../live/credential_live/edit.ex | 26 +++++++++ .../live/credential_live/edit.html.heex | 14 +++++ .../live/credential_live/form_component.ex | 58 +++++++++++++++++++ .../credential_live/form_component.html.heex | 34 +++++++++++ .../live/credential_live/index.ex | 50 ++++++++++++++++ .../live/credential_live/index.html.heex | 46 +++++++++++++++ .../live/credential_live/show.ex | 25 ++++++++ .../live/credential_live/show.html.heex | 34 +++++++++++ .../live/job_live/form_component.ex | 2 +- lib/lightning_web/router.ex | 1 + .../templates/layout/top_bar.html.heex | 5 ++ test/lightning/credentials_test.exs | 9 ++- 16 files changed, 351 insertions(+), 34 deletions(-) create mode 100644 lib/lightning/helpers.ex create mode 100644 lib/lightning_web/live/credential_live/edit.ex create mode 100644 lib/lightning_web/live/credential_live/edit.html.heex create mode 100644 lib/lightning_web/live/credential_live/form_component.ex create mode 100644 lib/lightning_web/live/credential_live/form_component.html.heex create mode 100644 lib/lightning_web/live/credential_live/index.ex create mode 100644 lib/lightning_web/live/credential_live/index.html.heex create mode 100644 lib/lightning_web/live/credential_live/show.ex create mode 100644 lib/lightning_web/live/credential_live/show.html.heex diff --git a/lib/lightning/credentials.ex b/lib/lightning/credentials.ex index de42f3bb0f..37cb9ffa98 100644 --- a/lib/lightning/credentials.ex +++ b/lib/lightning/credentials.ex @@ -4,6 +4,7 @@ defmodule Lightning.Credentials do """ import Ecto.Query, warn: false + import Lightning.Helpers, only: [coerce_json_field: 2] alias Lightning.Repo alias Lightning.Credentials.Credential @@ -51,7 +52,7 @@ defmodule Lightning.Credentials do """ def create_credential(attrs \\ %{}) do %Credential{} - |> Credential.changeset(attrs) + |> Credential.changeset(attrs |> coerce_json_field("body")) |> Repo.insert() end @@ -69,7 +70,7 @@ defmodule Lightning.Credentials do """ def update_credential(%Credential{} = credential, attrs) do credential - |> Credential.changeset(attrs) + |> Credential.changeset(attrs |> coerce_json_field("body")) |> Repo.update() end @@ -99,6 +100,9 @@ defmodule Lightning.Credentials do """ def change_credential(%Credential{} = credential, attrs \\ %{}) do - Credential.changeset(credential, attrs) + Credential.changeset( + credential, + attrs |> coerce_json_field("body") + ) end end diff --git a/lib/lightning/credentials/credential.ex b/lib/lightning/credentials/credential.ex index e4d5e22c4c..6140b62db4 100644 --- a/lib/lightning/credentials/credential.ex +++ b/lib/lightning/credentials/credential.ex @@ -1,4 +1,8 @@ defmodule Lightning.Credentials.Credential do + @moduledoc """ + The Credential model. + """ + use Ecto.Schema import Ecto.Changeset diff --git a/lib/lightning/helpers.ex b/lib/lightning/helpers.ex new file mode 100644 index 0000000000..a4988903cd --- /dev/null +++ b/lib/lightning/helpers.ex @@ -0,0 +1,35 @@ +defmodule Lightning.Helpers do + @moduledoc """ + Common functions for the context + """ + + @doc """ + Changes a given maps field from a json string to a map. + If it cannot be converted, it leaves the original value + """ + @spec coerce_json_field(map(), Map.key()) :: map() + def coerce_json_field(attrs, field) do + {_, attrs} = + Map.get_and_update(attrs, field, fn body -> + case body do + nil -> + :pop + + body when is_binary(body) -> + {body, decode_and_replace(body)} + + any -> + {body, any} + end + end) + + attrs + end + + defp decode_and_replace(body) do + case Jason.decode(body) do + {:error, _} -> body + {:ok, body_map} -> body_map + end + end +end diff --git a/lib/lightning/invocation.ex b/lib/lightning/invocation.ex index ffac6245f0..16ea35a438 100644 --- a/lib/lightning/invocation.ex +++ b/lib/lightning/invocation.ex @@ -4,6 +4,7 @@ defmodule Lightning.Invocation do """ import Ecto.Query, warn: false + import Lightning.Helpers, only: [coerce_json_field: 2] alias Lightning.Repo alias Lightning.Invocation.{Dataclip, Event, Run} @@ -73,7 +74,7 @@ defmodule Lightning.Invocation do """ def create_dataclip(attrs \\ %{}) do %Dataclip{} - |> Dataclip.changeset(attrs |> coerce_json_body()) + |> Dataclip.changeset(attrs |> coerce_json_field("body")) |> Repo.insert() end @@ -91,7 +92,7 @@ defmodule Lightning.Invocation do """ def update_dataclip(%Dataclip{} = dataclip, attrs) do dataclip - |> Dataclip.changeset(attrs |> coerce_json_body()) + |> Dataclip.changeset(attrs |> coerce_json_field("body")) |> Repo.update() end @@ -121,32 +122,7 @@ defmodule Lightning.Invocation do """ def change_dataclip(%Dataclip{} = dataclip, attrs \\ %{}) do - Dataclip.changeset(dataclip, attrs |> coerce_json_body()) - end - - defp coerce_json_body(attrs) do - {_, attrs} = - Map.get_and_update(attrs, "body", fn body -> - case body do - nil -> - :pop - - body when is_binary(body) -> - {body, decode_and_replace(body)} - - any -> - {body, any} - end - end) - - attrs - end - - def decode_and_replace(body) do - case Jason.decode(body) do - {:error, _} -> body - {:ok, body_map} -> body_map - end + Dataclip.changeset(dataclip, attrs |> coerce_json_field("body")) end @doc """ diff --git a/lib/lightning_web/live/credential_live/edit.ex b/lib/lightning_web/live/credential_live/edit.ex new file mode 100644 index 0000000000..da44a5418a --- /dev/null +++ b/lib/lightning_web/live/credential_live/edit.ex @@ -0,0 +1,26 @@ +defmodule LightningWeb.CredentialLive.Edit do + @moduledoc """ + LiveView for editing a single job, which inturn uses `LightningWeb.JobLive.FormComponent` + for common functionality. + """ + use LightningWeb, :live_view + + alias Lightning.Credentials + + @impl true + def mount(_params, _session, socket) do + {:ok, socket} + end + + @impl true + def handle_params(%{"id" => id}, _, socket) do + {:noreply, + socket + |> assign(:page_title, page_title(socket.assigns.live_action)) + |> assign(:active_menu_item, :credentials) + |> assign(:credentials, Credentials.get_credential!(id))} + end + + defp page_title(:show), do: "Show Job" + defp page_title(:edit), do: "Edit Job" +end diff --git a/lib/lightning_web/live/credential_live/edit.html.heex b/lib/lightning_web/live/credential_live/edit.html.heex new file mode 100644 index 0000000000..2489e385dc --- /dev/null +++ b/lib/lightning_web/live/credential_live/edit.html.heex @@ -0,0 +1,14 @@ + + + + + <.live_component + module={LightningWeb.CredentialLive.FormComponent} + id={@credential.id || :new} + title={@page_title} + action={@live_action} + credential={@credential} + return_to={Routes.credential_index_path(@socket, :index)} + /> + + diff --git a/lib/lightning_web/live/credential_live/form_component.ex b/lib/lightning_web/live/credential_live/form_component.ex new file mode 100644 index 0000000000..e5d9a8d199 --- /dev/null +++ b/lib/lightning_web/live/credential_live/form_component.ex @@ -0,0 +1,58 @@ +defmodule LightningWeb.CredentialLive.FormComponent do + @moduledoc """ + Form Component for working with a single Credential + """ + use LightningWeb, :live_component + + alias Lightning.{Credentials} + + @impl true + def update(%{credential: credential} = assigns, socket) do + changeset = Credentials.change_credential(credential) + + {:ok, + socket + |> assign(assigns) + |> assign(:changeset, changeset)} + end + + @impl true + def handle_event("validate", %{"credential" => credential_params}, socket) do + changeset = + socket.assigns.credential + |> Credentials.change_credential(credential_params) + |> Map.put(:action, :validate) + + {:noreply, assign(socket, :changeset, changeset)} + end + + def handle_event("save", %{"credential" => credential_params}, socket) do + save_credential(socket, socket.assigns.action, credential_params) + end + + defp save_credential(socket, :edit, credential_params) do + case Credentials.update_credential(socket.assigns.credential, credential_params) do + {:ok, _credential} -> + {:noreply, + socket + |> put_flash(:info, "Credential updated successfully") + |> push_redirect(to: socket.assigns.return_to)} + + {:error, %Ecto.Changeset{} = changeset} -> + {:noreply, assign(socket, :changeset, changeset)} + end + end + + defp save_credential(socket, :new, credential_params) do + case Credentials.create_credential(credential_params) do + {:ok, _credential} -> + {:noreply, + socket + |> put_flash(:info, "Credential created successfully") + |> push_redirect(to: socket.assigns.return_to)} + + {:error, %Ecto.Changeset{} = changeset} -> + {:noreply, assign(socket, changeset: changeset)} + end + end +end diff --git a/lib/lightning_web/live/credential_live/form_component.html.heex b/lib/lightning_web/live/credential_live/form_component.html.heex new file mode 100644 index 0000000000..f063bca6d9 --- /dev/null +++ b/lib/lightning_web/live/credential_live/form_component.html.heex @@ -0,0 +1,34 @@ +
+ <.form + let={f} + for={@changeset} + id="credential-form" + phx-target={@myself} + phx-change="validate" + phx-submit="save"> + +
+
+ + <%= label f, :name, class: "block text-sm font-medium text-gray-700" %> + <%= error_tag f, :name, class: "block w-full rounded-md" %> + <%= text_input f, :name, class: "mt-1 focus:ring-indigo-500 focus:border-indigo-500 block w-full shadow-sm sm:text-sm border-gray-300 rounded-md" %> + +
+ +
+ + + + <%= error_tag f, :body %> + <%= textarea f, :body, class: "rounded-md w-full font-mono bg-slate-800 text-slate-50 h-96 min-h-full" %> + +
+ <%= submit "Save", phx_disable_with: "Saving...", class: "inline-flex justify-center py-2 px-4 border border-transparent shadow-sm text-sm font-medium rounded-md text-white bg-indigo-600 hover:bg-indigo-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500" %> +
+ +
diff --git a/lib/lightning_web/live/credential_live/index.ex b/lib/lightning_web/live/credential_live/index.ex new file mode 100644 index 0000000000..f028abbc19 --- /dev/null +++ b/lib/lightning_web/live/credential_live/index.ex @@ -0,0 +1,50 @@ +defmodule LightningWeb.CredentialLive.Index do + @moduledoc """ + LiveView for listing and managing Jobs + """ + use LightningWeb, :live_view + + alias Lightning.Credentials + alias Lightning.Credentials.Credential + + @impl true + def mount(_params, _session, socket) do + {:ok, + assign(socket, :credentials, list_credentials()) |> assign(:active_menu_item, :credentials)} + end + + @impl true + def handle_params(params, _url, socket) do + {:noreply, apply_action(socket, socket.assigns.live_action, params)} + end + + defp apply_action(socket, :edit, %{"id" => id}) do + socket + |> assign(:page_title, "Edit Credential") + |> assign(:credential, Credentials.get_credential!(id)) + end + + defp apply_action(socket, :new, _params) do + socket + |> assign(:page_title, "New Credential") + |> assign(:credential, %Credential{}) + end + + defp apply_action(socket, :index, _params) do + socket + |> assign(:page_title, "Listing Credentials") + |> assign(:credential, nil) + end + + @impl true + def handle_event("delete", %{"id" => id}, socket) do + credential = Credentials.get_credential!(id) + {:ok, _} = Credentials.delete_credential(credential) + + {:noreply, assign(socket, :credentials, list_credentials())} + end + + defp list_credentials do + Credentials.list_credentials() + end +end diff --git a/lib/lightning_web/live/credential_live/index.html.heex b/lib/lightning_web/live/credential_live/index.html.heex new file mode 100644 index 0000000000..aad69f0b3c --- /dev/null +++ b/lib/lightning_web/live/credential_live/index.html.heex @@ -0,0 +1,46 @@ + + + + + <%= if @live_action in [:new, :edit] do %> + <.modal return_to={Routes.credential_index_path(@socket, :index)}> + <.live_component + module={LightningWeb.CredentialLive.FormComponent} + id={@credential.id || :new} + title={@page_title} + action={@live_action} + credential={@credential} + return_to={Routes.credential_index_path(@socket, :index)} + /> + + <% end %> + + + + + + + + + + + + <%= for credential <- @credentials do %> + + + + + + + <% end %> + +
NameBody
<%= credential.name %><%= credential.body %> + <%= live_redirect "Show", to: Routes.credential_show_path(@socket, :show, credential) %> + <%= live_redirect "Edit", to: Routes.credential_edit_path(@socket, :edit, credential) %> + <%= link "Delete", to: "#", phx_click: "delete", phx_value_id: credential.id, data: [confirm: "Are you sure?"] %> +
+ + <%= live_patch "New Credential", to: Routes.credential_index_path(@socket, :new) %> + +
+ diff --git a/lib/lightning_web/live/credential_live/show.ex b/lib/lightning_web/live/credential_live/show.ex new file mode 100644 index 0000000000..f3ee35578f --- /dev/null +++ b/lib/lightning_web/live/credential_live/show.ex @@ -0,0 +1,25 @@ +defmodule LightningWeb.CredentialLive.Show do + @moduledoc """ + LiveView for viewing a single Job + """ + use LightningWeb, :live_view + + alias Lightning.Credentials + + @impl true + def mount(_params, _session, socket) do + {:ok, socket} + end + + @impl true + def handle_params(%{"id" => id}, _, socket) do + {:noreply, + socket + |> assign(:page_title, page_title(socket.assigns.live_action)) + |> assign(:active_menu_item, :credentials) + |> assign(:credential, Credentials.get_credential!(id))} + end + + defp page_title(:show), do: "Show Credential" + defp page_title(:edit), do: "Edit Credential" +end diff --git a/lib/lightning_web/live/credential_live/show.html.heex b/lib/lightning_web/live/credential_live/show.html.heex new file mode 100644 index 0000000000..ad8c888b8b --- /dev/null +++ b/lib/lightning_web/live/credential_live/show.html.heex @@ -0,0 +1,34 @@ + + + + <%= if @live_action in [:edit] do %> + <.modal return_to={Routes.credential_show_path(@socket, :show, @credential)}> + <.live_component + module={LightningWeb.CredentialLive.FormComponent} + id={@credential.id} + title={@page_title} + action={@live_action} + credential={@credential} + return_to={Routes.credential_show_path(@socket, :show, @credential)} + /> + + <% end %> + +
    + +
  • + Name: + <%= @credential.name %> +
  • + +
  • + Body: + <%= @credential.body %> +
  • + +
+ + <%= live_patch "Edit", to: Routes.credential_edit_path(@socket, :edit, @credential), class: "button" %> | + <%= live_redirect "Back", to: Routes.credential_index_path(@socket, :index) %> +
+ diff --git a/lib/lightning_web/live/job_live/form_component.ex b/lib/lightning_web/live/job_live/form_component.ex index ef4440bc3b..1318803bf3 100644 --- a/lib/lightning_web/live/job_live/form_component.ex +++ b/lib/lightning_web/live/job_live/form_component.ex @@ -32,7 +32,7 @@ defmodule LightningWeb.JobLive.FormComponent do @impl true def handle_event("validate", %{"job" => job_params}, socket) do - # Coerce any changes to the "Adaptor" dropdown into a new selection on the + # Coerce any changes to the "Adaptor" dropdown into a new selection on the # Version dropdown. job_params = Map.update(job_params, "adaptor", "", fn _adaptor -> diff --git a/lib/lightning_web/router.ex b/lib/lightning_web/router.ex index d927afcd6e..fa7fb3acf0 100644 --- a/lib/lightning_web/router.ex +++ b/lib/lightning_web/router.ex @@ -1,6 +1,7 @@ defmodule LightningWeb.Router do use LightningWeb, :router alias JobLive + alias CredentialLive pipeline :browser do plug(:accepts, ["html"]) diff --git a/lib/lightning_web/templates/layout/top_bar.html.heex b/lib/lightning_web/templates/layout/top_bar.html.heex index 13ca4852d7..30363457b5 100644 --- a/lib/lightning_web/templates/layout/top_bar.html.heex +++ b/lib/lightning_web/templates/layout/top_bar.html.heex @@ -9,6 +9,11 @@ do: "bg-gray-900 text-white px-3 py-2 rounded-md text-sm font-medium", else: "text-gray-300 hover:bg-gray-700 hover:text-white px-3 py-2 rounded-md text-sm font-medium" %> + <%= live_redirect "Credentials", to: Routes.credential_index_path(@socket, :index), + class: if assigns[:active_menu_item] == :credentials, + do: "bg-gray-900 text-white px-3 py-2 rounded-md text-sm font-medium", + else: "text-gray-300 hover:bg-gray-700 hover:text-white px-3 py-2 rounded-md text-sm font-medium" %> + <%= live_redirect "Dataclips", to: Routes.dataclip_index_path(@socket, :index), class: if assigns[:active_menu_item] == :dataclips, do: "bg-gray-900 text-white px-3 py-2 rounded-md text-sm font-medium", diff --git a/test/lightning/credentials_test.exs b/test/lightning/credentials_test.exs index 1e62783e1e..a886bda833 100644 --- a/test/lightning/credentials_test.exs +++ b/test/lightning/credentials_test.exs @@ -36,14 +36,19 @@ defmodule Lightning.CredentialsTest do credential = credential_fixture() update_attrs = %{body: %{}, name: "some updated name"} - assert {:ok, %Credential{} = credential} = Credentials.update_credential(credential, update_attrs) + assert {:ok, %Credential{} = credential} = + Credentials.update_credential(credential, update_attrs) + assert credential.body == %{} assert credential.name == "some updated name" end test "update_credential/2 with invalid data returns error changeset" do credential = credential_fixture() - assert {:error, %Ecto.Changeset{}} = Credentials.update_credential(credential, @invalid_attrs) + + assert {:error, %Ecto.Changeset{}} = + Credentials.update_credential(credential, @invalid_attrs) + assert credential == Credentials.get_credential!(credential.id) end From 5ce7dd089a1d13d589dc5fb23c44f05020a28864 Mon Sep 17 00:00:00 2001 From: "Elias W. BA" Date: Thu, 24 Mar 2022 17:28:37 +0000 Subject: [PATCH 4/5] Add tests for coerce_json_field --- test/lightning/helpers_test.exs | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) create mode 100644 test/lightning/helpers_test.exs diff --git a/test/lightning/helpers_test.exs b/test/lightning/helpers_test.exs new file mode 100644 index 0000000000..daa2d46570 --- /dev/null +++ b/test/lightning/helpers_test.exs @@ -0,0 +1,27 @@ +defmodule Lightning.HelpersTest do + use ExUnit.Case, async: true + + import Lightning.Helpers, only: [coerce_json_field: 2] + + test "coerce_json_field/2 will transform a json string inside a map by it's key" do + input = %{ + "body" => + "{\n \"a\": 1,\n \"b\": {\n \"sadio\": true, \"other\": [1,2,\"bah\"]\n }\n}", + "name" => "My Credential" + } + + assert coerce_json_field(input, "body") == %{ + "body" => %{"a" => 1, "b" => %{"other" => [1, 2, "bah"], "sadio" => true}}, + "name" => "My Credential" + } + end + + test "coerce_json_field/2 will not do anything if the json string inside a map is invalid" do + input = %{name: "Sadio Mane", stats: "goals:126, teams: Metz, Liverpool"} + + assert coerce_json_field(input, "stats") == %{ + name: "Sadio Mane", + stats: "goals:126, teams: Metz, Liverpool" + } + end +end From 355dce5dfbb61071abab9628c8683d2e859607fb Mon Sep 17 00:00:00 2001 From: "Elias W. BA" Date: Sun, 27 Mar 2022 18:25:07 +0000 Subject: [PATCH 5/5] Add more test cases for coerce_json_field. Test credential.ex --- .../lightning/credentials/credential_test.exs | 13 +++++++++ test/lightning/helpers_test.exs | 27 +++++++++++++++++++ 2 files changed, 40 insertions(+) create mode 100644 test/lightning/credentials/credential_test.exs diff --git a/test/lightning/credentials/credential_test.exs b/test/lightning/credentials/credential_test.exs new file mode 100644 index 0000000000..9aa8c7c930 --- /dev/null +++ b/test/lightning/credentials/credential_test.exs @@ -0,0 +1,13 @@ +defmodule Lightning.Credentials.CredentialTest do + use Lightning.DataCase + + alias Lightning.Credentials.Credential + + describe "changeset/2" do + test "name can't be blank" do + errors = Credential.changeset(%Credential{}, %{}) |> errors_on() + assert errors[:name] == ["can't be blank"] + assert errors[:body] == ["can't be blank"] + end + end +end diff --git a/test/lightning/helpers_test.exs b/test/lightning/helpers_test.exs index daa2d46570..439a43eb73 100644 --- a/test/lightning/helpers_test.exs +++ b/test/lightning/helpers_test.exs @@ -24,4 +24,31 @@ defmodule Lightning.HelpersTest do stats: "goals:126, teams: Metz, Liverpool" } end + + test "coerce_json_field/2 will not do anything if the given key doesn't exist" do + input = %{name: "Sadio Mane", stats: "goals:126, teams: Metz, Liverpool"} + + assert coerce_json_field(input, "somekey") == %{ + name: "Sadio Mane", + stats: "goals:126, teams: Metz, Liverpool" + } + end + + test "coerce_json_field/2 will not do anything if the value of the given key is nil" do + input = %{name: "Sadio Mane", stats: nil} + + assert coerce_json_field(input, "stats") == %{ + name: "Sadio Mane", + stats: nil + } + end + + test "coerce_json_field/2 will not do anything if the value of the given key is not a string" do + input = %{name: "Sadio Mane", goals: 123} + + assert coerce_json_field(input, "goals") == %{ + name: "Sadio Mane", + goals: 123 + } + end end