+
{@title}
<%= if Enum.empty?(@clients) do %>
{render_slot(@empty_state)}
<% else %>
- <.table id={"#{@id}-table"}>
+ <.table id={"#{@id}-table"} page={@page} url={@url}>
<:header>
<.tr>
<.th>Name
diff --git a/lib/lightning_web/live/credential_live/credential_index_component.ex b/lib/lightning_web/live/credential_live/credential_index_component.ex
index 2005360b7db..872452ad1a3 100644
--- a/lib/lightning_web/live/credential_live/credential_index_component.ex
+++ b/lib/lightning_web/live/credential_live/credential_index_component.ex
@@ -7,6 +7,14 @@ defmodule LightningWeb.CredentialLive.CredentialIndexComponent do
alias Lightning.OauthClients
alias Lightning.Policies
+ @empty_page %{
+ entries: [],
+ page_size: 0,
+ total_entries: 0,
+ page_number: 1,
+ total_pages: 0
+ }
+
@impl true
def mount(socket) do
{:ok,
@@ -15,11 +23,15 @@ defmodule LightningWeb.CredentialLive.CredentialIndexComponent do
can_create_keychain_credential: false,
can_create_project_credential: false,
credential: nil,
- credentials: [],
+ credentials_page: @empty_page,
+ credentials_url: nil,
current_user: nil,
- keychain_credentials: nil,
+ keychain_credentials_page: @empty_page,
+ keychain_url: nil,
oauth_client: nil,
- oauth_clients: [],
+ oauth_clients_page: @empty_page,
+ oauth_clients_expanded: false,
+ oauth_clients_url: nil,
project: nil,
project_user: nil,
projects: [],
@@ -45,8 +57,7 @@ defmodule LightningWeb.CredentialLive.CredentialIndexComponent do
current_user,
%{project: project, project_user: project_user}
)
- })
- |> load_credentials()}
+ })}
end
@impl true
@@ -54,38 +65,18 @@ defmodule LightningWeb.CredentialLive.CredentialIndexComponent do
%{current_user: _, projects: _, return_to: _} = assigns,
socket
) do
- {:ok, socket |> assign(assigns) |> load_credentials()}
- end
-
- defp load_credentials(socket) do
- %{current_user: current_user, project: project} = socket.assigns
-
- socket
- |> assign(%{
- credentials: list_credentials(project || current_user),
- oauth_clients: list_clients(project || current_user)
- })
- |> then(fn socket ->
- if socket.assigns.project do
- socket
- |> assign(
- :keychain_credentials,
- Lightning.Credentials.list_keychain_credentials_for_project(
- socket.assigns.project
- )
- )
- else
- socket
- end
- end)
+ {:ok, socket |> assign(assigns)}
end
@impl true
def handle_event("close_active_modal", _params, socket) do
{:noreply,
socket
- |> assign(active_modal: nil, credential: nil, oauth_client: nil)
- |> load_credentials()}
+ |> assign(active_modal: nil, credential: nil, oauth_client: nil)}
+ end
+
+ def handle_event("toggle_oauth_clients", _params, socket) do
+ {:noreply, update(socket, :oauth_clients_expanded, &(!&1))}
end
def handle_event("show_modal", %{"target" => "new_credential"}, socket) do
@@ -164,7 +155,7 @@ defmodule LightningWeb.CredentialLive.CredentialIndexComponent do
end
def handle_event("edit_oauth_client", %{"id" => client_id}, socket) do
- %{oauth_clients: oauth_clients} = socket.assigns
+ oauth_clients = socket.assigns.oauth_clients_page.entries
client = Enum.find(oauth_clients, fn client -> client.id == client_id end)
if can_edit_credential(socket.assigns.current_user, client) do
@@ -180,7 +171,7 @@ defmodule LightningWeb.CredentialLive.CredentialIndexComponent do
end
def handle_event("request_oauth_client_deletion", %{"id" => client_id}, socket) do
- %{oauth_clients: oauth_clients} = socket.assigns
+ oauth_clients = socket.assigns.oauth_clients_page.entries
client = Enum.find(oauth_clients, fn client -> client.id == client_id end)
if can_edit_credential(socket.assigns.current_user, client) do
@@ -216,7 +207,7 @@ defmodule LightningWeb.CredentialLive.CredentialIndexComponent do
end
def handle_event("edit_credential", %{"id" => credential_id}, socket) do
- %{credentials: credentials} = socket.assigns
+ credentials = socket.assigns.credentials_page.entries
credential = Enum.find(credentials, fn cred -> cred.id == credential_id end)
if can_edit_credential(socket.assigns.current_user, credential) do
@@ -236,7 +227,8 @@ defmodule LightningWeb.CredentialLive.CredentialIndexComponent do
%{"id" => credential_id},
socket
) do
- %{current_user: current_user, credentials: credentials} = socket.assigns
+ %{current_user: current_user} = socket.assigns
+ credentials = socket.assigns.credentials_page.entries
credential = Enum.find(credentials, &(&1.id == credential_id))
if credential && can_delete_credential(current_user, credential) do
@@ -252,7 +244,8 @@ defmodule LightningWeb.CredentialLive.CredentialIndexComponent do
%{"id" => credential_id},
socket
) do
- %{current_user: current_user, credentials: credentials} = socket.assigns
+ %{current_user: current_user} = socket.assigns
+ credentials = socket.assigns.credentials_page.entries
credential = Enum.find(credentials, &(&1.id == credential_id))
if credential && can_edit_credential(current_user, credential) do
@@ -268,8 +261,9 @@ defmodule LightningWeb.CredentialLive.CredentialIndexComponent do
%{"id" => keychain_credential_id},
socket
) do
- %{current_user: current_user, keychain_credentials: keychain_credentials} =
- socket.assigns
+ %{current_user: current_user} = socket.assigns
+
+ keychain_credentials = socket.assigns.keychain_credentials_page.entries
credential =
Enum.find(keychain_credentials, &(&1.id == keychain_credential_id))
@@ -291,7 +285,7 @@ defmodule LightningWeb.CredentialLive.CredentialIndexComponent do
%{"id" => keychain_credential_id},
socket
) do
- %{keychain_credentials: keychain_credentials} = socket.assigns
+ keychain_credentials = socket.assigns.keychain_credentials_page.entries
credential =
Enum.find(keychain_credentials, &(&1.id == keychain_credential_id))
@@ -324,14 +318,12 @@ defmodule LightningWeb.CredentialLive.CredentialIndexComponent do
if credential && can_delete_credential(current_user, credential) do
Lightning.Credentials.delete_keychain_credential(credential)
|> case do
- {:ok, %{id: id}} ->
+ {:ok, _deleted} ->
{:noreply,
socket
- |> update(:keychain_credentials, fn credentials ->
- credentials |> Enum.reject(&(&1.id == id))
- end)
|> push_event("close_modal", %{id: modal_id})
- |> put_flash(:info, "Keychain credential deleted")}
+ |> put_flash(:info, "Keychain credential deleted")
+ |> push_patch(to: socket.assigns.return_to)}
{:error, _} ->
{:noreply,
@@ -403,39 +395,6 @@ defmodule LightningWeb.CredentialLive.CredentialIndexComponent do
|> push_patch(to: socket.assigns.return_to)}
end
- defp list_credentials(user_or_project) do
- user_or_project
- |> Credentials.list_credentials()
- |> Enum.map(fn credential ->
- project_names =
- Map.get(credential, :projects, []) |> Enum.map(fn p -> p.name end)
-
- environment_names =
- credential
- |> Map.get(:credential_bodies, [])
- |> Enum.map(& &1.name)
-
- credential
- |> Map.put(:project_names, project_names)
- |> Map.put(:environment_names, environment_names)
- end)
- end
-
- defp list_clients(user_or_project) do
- user_or_project
- |> OauthClients.list_clients()
- |> Enum.map(fn c ->
- project_names =
- if c.global,
- do: ["GLOBAL"],
- else:
- Map.get(c, :projects, [])
- |> Enum.map(fn p -> p.name end)
-
- Map.put(c, :project_names, project_names)
- end)
- end
-
defp delete_action(assigns) do
~H"""
<%= if @credential.scheduled_deletion do %>
diff --git a/lib/lightning_web/live/credential_live/credential_index_component.html.heex b/lib/lightning_web/live/credential_live/credential_index_component.html.heex
index 74815de39b0..e2620d66021 100644
--- a/lib/lightning_web/live/credential_live/credential_index_component.html.heex
+++ b/lib/lightning_web/live/credential_live/credential_index_component.html.heex
@@ -1,60 +1,12 @@
-
- <:empty_state>
- <.empty_state
- icon="hero-plus-circle"
- message="No OAuth clients found."
- button_text="Create a new OAuth client"
- button_id="open-create-oauth-client-modal-big-buttton"
- phx-target={@myself}
- phx-click="show_modal"
- phx-value-target="new_oauth_client"
- button_disabled={false}
- />
-
- <:actions :let={client}>
-
-
- <:button>
- Actions
-
-
- <:options>
- <.link
- id={"oauth-client-actions-#{client.id}-edit"}
- phx-click="edit_oauth_client"
- phx-value-id={client.id}
- phx-target={@myself}
- >
- Edit
-
- <.link
- id={"oauth-client-actions-#{client.id}-delete"}
- phx-click="request_oauth_client_deletion"
- phx-value-id={client.id}
- phx-target={@myself}
- >
- Delete
-
-
-
-
-
-
<:empty_state>
<.empty_state
@@ -111,12 +63,15 @@
- <%= if @keychain_credentials do %>
+
+ <%= if @project do %>
<:empty_state>
<.empty_state
@@ -163,6 +118,83 @@
<% end %>
+
+
+
+
+ <%= if @oauth_clients_expanded do %>
+
+
+ <:empty_state>
+ <.empty_state
+ icon="hero-plus-circle"
+ message="No OAuth clients found."
+ button_text="Create a new OAuth client"
+ button_id="open-create-oauth-client-modal-big-buttton"
+ phx-target={@myself}
+ phx-click="show_modal"
+ phx-value-target="new_oauth_client"
+ button_disabled={false}
+ />
+
+ <:actions :let={client}>
+
+
+ <:button>
+ Actions
+
+
+ <:options>
+ <.link
+ id={"oauth-client-actions-#{client.id}-edit"}
+ phx-click="edit_oauth_client"
+ phx-value-id={client.id}
+ phx-target={@myself}
+ >
+ Edit
+
+ <.link
+ id={"oauth-client-actions-#{client.id}-delete"}
+ phx-click="request_oauth_client_deletion"
+ phx-value-id={client.id}
+ phx-target={@myself}
+ >
+ Delete
+
+
+
+
+
+
+
+ <% end %>
+
<.live_component
:if={@active_modal == :new_oauth_client}
@@ -202,7 +234,7 @@
credential_type={nil}
credential={@credential}
oauth_client={nil}
- oauth_clients={@oauth_clients}
+ oauth_clients={@oauth_clients_page.entries}
project={@project}
projects={@projects}
current_user={@current_user}
@@ -218,7 +250,7 @@
action={:edit}
keychain_credential={@credential}
project={@project}
- credentials={@credentials}
+ credentials={@credentials_page.entries}
current_user={@current_user}
project_user={@project_user}
return_to={@return_to}
@@ -231,6 +263,7 @@
credential_type={nil}
credential={@credential}
oauth_client={@oauth_client}
+ oauth_clients={@oauth_clients_page.entries}
project={@project}
projects={@projects}
current_user={@current_user}
@@ -261,7 +294,7 @@
action={:new}
keychain_credential={@credential}
project={@project}
- credentials={@credentials}
+ credentials={@credentials_page.entries}
current_user={@current_user}
project_user={@project_user}
return_to={@return_to}
diff --git a/lib/lightning_web/live/credential_live/index.ex b/lib/lightning_web/live/credential_live/index.ex
index eab6e6b647f..e6cc8784101 100644
--- a/lib/lightning_web/live/credential_live/index.ex
+++ b/lib/lightning_web/live/credential_live/index.ex
@@ -4,6 +4,9 @@ defmodule LightningWeb.CredentialLive.Index do
"""
use LightningWeb, :live_view
+ alias Lightning.Credentials
+ alias Lightning.OauthClients
+
on_mount {LightningWeb.Hooks, :assign_projects}
@impl true
@@ -21,9 +24,39 @@ defmodule LightningWeb.CredentialLive.Index do
{:noreply, apply_action(socket, socket.assigns.live_action, params)}
end
- defp apply_action(socket, :index, _params) do
+ defp apply_action(socket, :index, params) do
+ current_user = socket.assigns.current_user
+ creds_params = %{"page" => params["credentials_page"] || "1"}
+ oauth_params = %{"page" => params["oauth_clients_page"] || "1"}
+
+ credentials_page =
+ Credentials.list_credentials(current_user, creds_params)
+ |> map_credentials()
+
+ oauth_clients_page =
+ OauthClients.list_clients(current_user, oauth_params)
+ |> map_oauth_clients()
+
socket
- |> assign(credential: nil)
+ |> assign(:credential, nil)
+ |> assign(:credentials_page, credentials_page)
+ |> assign(:oauth_clients_page, oauth_clients_page)
+ |> assign(
+ :credentials_url,
+ fn opts ->
+ Routes.credential_index_path(socket, :index,
+ credentials_page: opts[:page]
+ )
+ end
+ )
+ |> assign(
+ :oauth_clients_url,
+ fn opts ->
+ Routes.credential_index_path(socket, :index,
+ oauth_clients_page: opts[:page]
+ )
+ end
+ )
end
@doc """
@@ -34,4 +67,32 @@ defmodule LightningWeb.CredentialLive.Index do
send_update(mod, opts)
{:noreply, socket}
end
+
+ defp map_credentials(%Scrivener.Page{} = page) do
+ %{page | entries: Enum.map(page.entries, &add_credential_display_fields/1)}
+ end
+
+ defp add_credential_display_fields(credential) do
+ project_names = Map.get(credential, :projects, []) |> Enum.map(& &1.name)
+
+ environment_names =
+ credential |> Map.get(:credential_bodies, []) |> Enum.map(& &1.name)
+
+ credential
+ |> Map.put(:project_names, project_names)
+ |> Map.put(:environment_names, environment_names)
+ end
+
+ defp map_oauth_clients(%Scrivener.Page{} = page) do
+ %{page | entries: Enum.map(page.entries, &add_oauth_client_display_fields/1)}
+ end
+
+ defp add_oauth_client_display_fields(client) do
+ project_names =
+ if client.global,
+ do: ["GLOBAL"],
+ else: Map.get(client, :projects, []) |> Enum.map(& &1.name)
+
+ Map.put(client, :project_names, project_names)
+ end
end
diff --git a/lib/lightning_web/live/credential_live/index.html.heex b/lib/lightning_web/live/credential_live/index.html.heex
index acd3e10e8d1..3fc10ad45d7 100644
--- a/lib/lightning_web/live/credential_live/index.html.heex
+++ b/lib/lightning_web/live/credential_live/index.html.heex
@@ -33,6 +33,10 @@
can_create_project_credential={true}
show_owner_in_tables={false}
return_to={~p"/credentials"}
+ credentials_page={@credentials_page}
+ oauth_clients_page={@oauth_clients_page}
+ credentials_url={@credentials_url}
+ oauth_clients_url={@oauth_clients_url}
/>
diff --git a/lib/lightning_web/live/project_live/settings.ex b/lib/lightning_web/live/project_live/settings.ex
index 33c51e3fa8e..17beb79d327 100644
--- a/lib/lightning_web/live/project_live/settings.ex
+++ b/lib/lightning_web/live/project_live/settings.ex
@@ -10,6 +10,7 @@ defmodule LightningWeb.ProjectLive.Settings do
alias Lightning.Accounts.User
alias Lightning.Credentials
alias Lightning.Helpers
+ alias Lightning.OauthClients
alias Lightning.Policies.Permissions
alias Lightning.Projects
alias Lightning.Projects.Project
@@ -128,9 +129,33 @@ defmodule LightningWeb.ProjectLive.Settings do
active_menu_item: :settings,
can_receive_failure_alerts: can_receive_failure_alerts,
collaborators_to_invite: [],
+ credentials_page: %{
+ entries: [],
+ page_size: 0,
+ total_entries: 0,
+ page_number: 1,
+ total_pages: 0
+ },
+ credentials_url: nil,
current_user: socket.assigns.current_user,
github_enabled: VersionControl.github_enabled?(),
+ keychain_credentials_page: %{
+ entries: [],
+ page_size: 0,
+ total_entries: 0,
+ page_number: 1,
+ total_pages: 0
+ },
+ keychain_url: nil,
name: project.name,
+ oauth_clients_page: %{
+ entries: [],
+ page_size: 0,
+ total_entries: 0,
+ page_number: 1,
+ total_pages: 0
+ },
+ oauth_clients_url: nil,
parent_project: parent_project,
root_project: root_project,
project: project,
@@ -161,9 +186,10 @@ defmodule LightningWeb.ProjectLive.Settings do
|> apply_action(live_action, params)}
end
- defp apply_action(socket, :index, _params) do
- project_users = Projects.get_project_users!(socket.assigns.project.id)
- auth_methods = WebhookAuthMethods.list_for_project(socket.assigns.project)
+ defp apply_action(socket, :index, params) do
+ project = socket.assigns.project
+ project_users = Projects.get_project_users!(project.id)
+ auth_methods = WebhookAuthMethods.list_for_project(project)
concurrency_input_component =
socket.router
@@ -174,6 +200,21 @@ defmodule LightningWeb.ProjectLive.Settings do
)
|> Map.get(:concurrency_input)
+ creds_params = %{"page" => params["credentials_page"] || "1"}
+ keychain_params = %{"page" => params["keychain_page"] || "1"}
+ oauth_params = %{"page" => params["oauth_clients_page"] || "1"}
+
+ credentials_page =
+ Credentials.list_credentials(project, creds_params)
+ |> map_credentials()
+
+ keychain_credentials_page =
+ Credentials.list_keychain_credentials_for_project(project, keychain_params)
+
+ oauth_clients_page =
+ OauthClients.list_clients(project, oauth_params)
+ |> map_oauth_clients()
+
socket
|> assign(
page_title: "Project settings",
@@ -185,6 +226,33 @@ defmodule LightningWeb.ProjectLive.Settings do
active_modal: nil,
active_modal_assigns: nil
)
+ |> assign(:credentials_page, credentials_page)
+ |> assign(:keychain_credentials_page, keychain_credentials_page)
+ |> assign(:oauth_clients_page, oauth_clients_page)
+ |> assign(
+ :credentials_url,
+ fn opts ->
+ Routes.project_settings_path(socket, :index, project.id,
+ credentials_page: opts[:page]
+ )
+ end
+ )
+ |> assign(
+ :keychain_url,
+ fn opts ->
+ Routes.project_settings_path(socket, :index, project.id,
+ keychain_page: opts[:page]
+ )
+ end
+ )
+ |> assign(
+ :oauth_clients_url,
+ fn opts ->
+ Routes.project_settings_path(socket, :index, project.id,
+ oauth_clients_page: opts[:page]
+ )
+ end
+ )
end
defp apply_action(socket, :delete, %{"project_id" => id}) do
@@ -797,6 +865,34 @@ defmodule LightningWeb.ProjectLive.Settings do
end
end
+ defp map_credentials(%Scrivener.Page{} = page) do
+ %{page | entries: Enum.map(page.entries, &add_credential_display_fields/1)}
+ end
+
+ defp add_credential_display_fields(credential) do
+ project_names = Map.get(credential, :projects, []) |> Enum.map(& &1.name)
+
+ environment_names =
+ credential |> Map.get(:credential_bodies, []) |> Enum.map(& &1.name)
+
+ credential
+ |> Map.put(:project_names, project_names)
+ |> Map.put(:environment_names, environment_names)
+ end
+
+ defp map_oauth_clients(%Scrivener.Page{} = page) do
+ %{page | entries: Enum.map(page.entries, &add_oauth_client_display_fields/1)}
+ end
+
+ defp add_oauth_client_display_fields(client) do
+ project_names =
+ if client.global,
+ do: ["GLOBAL"],
+ else: Map.get(client, :projects, []) |> Enum.map(& &1.name)
+
+ Map.put(client, :project_names, project_names)
+ end
+
attr :can_edit_project, :boolean, required: true
attr :project, :any, required: true
diff --git a/lib/lightning_web/live/project_live/settings.html.heex b/lib/lightning_web/live/project_live/settings.html.heex
index 296e9e9a698..872c56737ab 100644
--- a/lib/lightning_web/live/project_live/settings.html.heex
+++ b/lib/lightning_web/live/project_live/settings.html.heex
@@ -353,6 +353,12 @@
can_create_project_credential={@can_create_project_credential}
show_owner_in_tables={true}
return_to={~p"/projects/#{@project.id}/settings#credentials"}
+ credentials_page={@credentials_page}
+ keychain_credentials_page={@keychain_credentials_page}
+ oauth_clients_page={@oauth_clients_page}
+ credentials_url={@credentials_url}
+ keychain_url={@keychain_url}
+ oauth_clients_url={@oauth_clients_url}
/>
<:panel hash="collections" class="space-y-4">
diff --git a/test/lightning/credentials_test.exs b/test/lightning/credentials_test.exs
index e58ad8a5127..b49ecc76993 100644
--- a/test/lightning/credentials_test.exs
+++ b/test/lightning/credentials_test.exs
@@ -71,6 +71,86 @@ defmodule Lightning.CredentialsTest do
]
end
+ test "list_credentials/2 returns a paginated page for a user" do
+ user = insert(:user)
+ for i <- 1..12, do: insert(:credential, user: user, name: "cred-#{i}")
+
+ page =
+ Credentials.list_credentials(user, %{"page" => "1", "page_size" => "10"})
+
+ assert %Scrivener.Page{} = page
+ assert page.total_entries == 12
+ assert page.page_size == 10
+ assert length(page.entries) == 10
+
+ page2 =
+ Credentials.list_credentials(user, %{"page" => "2", "page_size" => "10"})
+
+ assert length(page2.entries) == 2
+ assert page2.page_number == 2
+ end
+
+ test "list_credentials/2 returns a paginated page for a project" do
+ user = insert(:user)
+ project = insert(:project, project_users: [%{user: user}])
+ other_project = insert(:project)
+
+ for i <- 1..12,
+ do:
+ insert(:credential,
+ user: user,
+ name: "cred-#{i}",
+ project_credentials: [%{project: project}]
+ )
+
+ insert(:credential,
+ user: user,
+ name: "other-cred",
+ project_credentials: [%{project: other_project}]
+ )
+
+ page =
+ Credentials.list_credentials(project, %{
+ "page" => "1",
+ "page_size" => "10"
+ })
+
+ assert %Scrivener.Page{} = page
+ assert page.total_entries == 12
+ assert length(page.entries) == 10
+ assert Enum.all?(page.entries, fn c -> c.id != "other-cred" end)
+ end
+
+ test "list_keychain_credentials_for_project/2 returns a paginated page" do
+ user = insert(:user)
+ project = insert(:project, project_users: [%{user: user}])
+ other_project = insert(:project)
+
+ for _i <- 1..12,
+ do: insert(:keychain_credential, project: project, created_by: user)
+
+ insert(:keychain_credential, project: other_project, created_by: user)
+
+ page =
+ Credentials.list_keychain_credentials_for_project(project, %{
+ "page" => "1",
+ "page_size" => "10"
+ })
+
+ assert %Scrivener.Page{} = page
+ assert page.total_entries == 12
+ assert length(page.entries) == 10
+ assert Enum.all?(page.entries, fn kc -> kc.project_id == project.id end)
+
+ page2 =
+ Credentials.list_keychain_credentials_for_project(project, %{
+ "page" => "2",
+ "page_size" => "10"
+ })
+
+ assert length(page2.entries) == 2
+ end
+
test "get_credential!/1 returns the credential with given id" do
user = insert(:user)
credential = insert(:credential, user_id: user.id)
diff --git a/test/lightning/oauth_clients_test.exs b/test/lightning/oauth_clients_test.exs
index 075ab54f8c2..f486655cec7 100644
--- a/test/lightning/oauth_clients_test.exs
+++ b/test/lightning/oauth_clients_test.exs
@@ -110,6 +110,57 @@ defmodule Lightning.OauthClientsTest do
end
end
+ describe "list_clients/2" do
+ test "returns a paginated page of oauth clients for a user" do
+ user = insert(:user)
+ for _i <- 1..12, do: insert(:oauth_client, user: user)
+
+ page =
+ OauthClients.list_clients(user, %{"page" => "1", "page_size" => "10"})
+
+ assert %Scrivener.Page{} = page
+ assert page.page_size == 10
+ assert page.total_entries >= 12
+ assert length(page.entries) == 10
+
+ page2 =
+ OauthClients.list_clients(user, %{"page" => "2", "page_size" => "10"})
+
+ assert page2.page_number == 2
+ assert length(page2.entries) > 0
+ end
+
+ test "returns a paginated page of oauth clients for a project, including globals" do
+ user = insert(:user)
+ project = insert(:project)
+ other_project = insert(:project)
+
+ project_clients =
+ for _i <- 1..3,
+ do:
+ insert(:oauth_client,
+ user: user,
+ project_oauth_clients: [%{project: project}]
+ )
+
+ new_global = insert(:oauth_client, global: true, user: user)
+
+ other_client =
+ insert(:oauth_client,
+ user: user,
+ project_oauth_clients: [%{project: other_project}]
+ )
+
+ page =
+ OauthClients.list_clients(project, %{"page" => "1", "page_size" => "100"})
+
+ assert %Scrivener.Page{} = page
+ assert client_id_in_list?(new_global, page.entries)
+ assert Enum.all?(project_clients, &client_id_in_list?(&1, page.entries))
+ refute client_id_in_list?(other_client, page.entries)
+ end
+ end
+
describe "create_client/1 with project association" do
test "successfully creates a client and associates with a project" do
user = insert(:user)
diff --git a/test/lightning_web/live/credential_live_test.exs b/test/lightning_web/live/credential_live_test.exs
index b1151cebcd5..3d1c2593df4 100644
--- a/test/lightning_web/live/credential_live_test.exs
+++ b/test/lightning_web/live/credential_live_test.exs
@@ -508,6 +508,176 @@ defmodule LightningWeb.CredentialLiveTest do
end
end
+ describe "CredentialIndexComponent pagination and collapsible" do
+ test "credentials table shows pagination bar and supports page navigation when there are more than 10 credentials",
+ %{conn: conn, user: user} do
+ for _i <- 1..12, do: insert(:credential, user: user)
+
+ {:ok, index_live, _html} = live(conn, ~p"/credentials", on_error: :raise)
+
+ table_html = index_live |> element("#credentials-table") |> render()
+
+ assert table_html =~ "Showing"
+ assert table_html =~ "12"
+
+ render_patch(index_live, ~p"/credentials?credentials_page=2")
+
+ table_html = index_live |> element("#credentials-table") |> render()
+
+ assert table_html =~ "Showing"
+ assert table_html =~ "12"
+ end
+
+ test "credentials table does not show page navigation links when there are 10 or fewer credentials",
+ %{conn: conn, user: user} do
+ for _i <- 1..5, do: insert(:credential, user: user)
+
+ {:ok, index_live, _html} = live(conn, ~p"/credentials", on_error: :raise)
+
+ table_html = index_live |> element("#credentials-table") |> render()
+
+ refute table_html =~ "sr-only\">Previous"
+ refute table_html =~ "sr-only\">Next"
+ end
+
+ test "OAuth clients section is collapsed by default and toggle button is visible",
+ %{conn: conn, user: user} do
+ oauth_client = insert(:oauth_client, user: user)
+
+ {:ok, index_live, html} = live(conn, ~p"/credentials", on_error: :raise)
+
+ assert has_element?(index_live, "#oauth-clients-section")
+ assert has_element?(index_live, "button[phx-click='toggle_oauth_clients']")
+ refute html =~ oauth_client.name
+ end
+
+ test "toggling OAuth clients section shows and then hides the clients table",
+ %{conn: conn, user: user} do
+ oauth_client = insert(:oauth_client, user: user)
+
+ {:ok, index_live, _html} = live(conn, ~p"/credentials", on_error: :raise)
+
+ html =
+ index_live
+ |> with_target("#credentials-index-component")
+ |> render_click("toggle_oauth_clients", %{})
+
+ assert html =~ oauth_client.name
+
+ html =
+ index_live
+ |> with_target("#credentials-index-component")
+ |> render_click("toggle_oauth_clients", %{})
+
+ refute html =~ oauth_client.name
+ end
+
+ test "OAuth clients table shows pagination bar after section is expanded and supports page navigation",
+ %{conn: conn, user: user} do
+ for _i <- 1..12, do: insert(:oauth_client, user: user)
+
+ {:ok, index_live, _html} = live(conn, ~p"/credentials", on_error: :raise)
+
+ index_live
+ |> with_target("#credentials-index-component")
+ |> render_click("toggle_oauth_clients", %{})
+
+ table_html = index_live |> element("#oauth-clients-table") |> render()
+
+ assert table_html =~ "Showing"
+ assert table_html =~ "12"
+
+ render_patch(index_live, ~p"/credentials?oauth_clients_page=2")
+
+ table_html = index_live |> element("#oauth-clients-table") |> render()
+
+ assert table_html =~ "Showing"
+ assert table_html =~ "12"
+ end
+
+ test "credentials pagination works on the project settings page",
+ %{conn: conn, user: user, project: project} do
+ for _i <- 1..12,
+ do:
+ insert(:credential,
+ user: user,
+ project_credentials: [%{project: project}]
+ )
+
+ {:ok, view, _html} =
+ live(conn, ~p"/projects/#{project}/settings#credentials",
+ on_error: :raise
+ )
+
+ table_html = view |> element("#credentials-table") |> render()
+
+ assert table_html =~ "Showing"
+ assert table_html =~ "12"
+
+ render_patch(view, ~p"/projects/#{project.id}/settings?credentials_page=2")
+
+ table_html = view |> element("#credentials-table") |> render()
+
+ assert table_html =~ "Showing"
+ assert table_html =~ "12"
+ end
+
+ test "keychain credentials table shows pagination on project settings when there are more than 10",
+ %{conn: conn, user: user, project: project} do
+ for _i <- 1..12,
+ do: insert(:keychain_credential, project: project, created_by: user)
+
+ {:ok, view, _html} =
+ live(conn, ~p"/projects/#{project}/settings#credentials",
+ on_error: :raise
+ )
+
+ table_html = view |> element("#keychain-credentials-table") |> render()
+
+ assert table_html =~ "Showing"
+ assert table_html =~ "12"
+
+ render_patch(view, ~p"/projects/#{project.id}/settings?keychain_page=2")
+
+ table_html = view |> element("#keychain-credentials-table") |> render()
+
+ assert table_html =~ "Showing"
+ assert table_html =~ "12"
+ end
+
+ test "keychain credentials section is not shown on the user credentials page",
+ %{conn: conn} do
+ {:ok, index_live, _html} = live(conn, ~p"/credentials", on_error: :raise)
+
+ refute has_element?(index_live, "#keychain-credentials-table")
+ end
+
+ test "keychain credentials section is shown on the project settings page",
+ %{conn: conn, user: user, project: project} do
+ insert(:keychain_credential, project: project, created_by: user)
+
+ {:ok, view, _html} =
+ live(conn, ~p"/projects/#{project}/settings#credentials",
+ on_error: :raise
+ )
+
+ assert has_element?(view, "#keychain-credentials-table")
+ end
+
+ test "navigating to a page number beyond total pages falls back gracefully",
+ %{conn: conn, user: user} do
+ for _i <- 1..5, do: insert(:credential, user: user)
+
+ {:ok, index_live, _html} = live(conn, ~p"/credentials", on_error: :raise)
+
+ render_patch(index_live, ~p"/credentials?credentials_page=999")
+
+ table_html = index_live |> element("#credentials-table") |> render()
+
+ assert table_html =~ "Showing"
+ end
+ end
+
describe "Clicking new from the list view" do
test "allows the user to define and save a new raw credential", %{
conn: conn,
diff --git a/test/lightning_web/live/oauth_clients_live_test.exs b/test/lightning_web/live/oauth_clients_live_test.exs
index 8a1f7dcede6..4e734a77d7c 100644
--- a/test/lightning_web/live/oauth_clients_live_test.exs
+++ b/test/lightning_web/live/oauth_clients_live_test.exs
@@ -253,33 +253,44 @@ defmodule LightningWeb.OauthClientsLiveTest do
{view, added_mandatory_scopes, added_optional_scopes} =
perforom_scopes_management_tests(view)
- {:ok, _view, html} =
+ {:ok, redirected_view, html} =
view
|> form("#oauth-client-form-new", oauth_client: valid_attrs)
|> render_submit()
|> follow_redirect(conn, url)
- assert html =~ valid_attrs.name
assert html =~ "Oauth client created successfully"
+ expanded_html =
+ redirected_view
+ |> with_target("#credentials-index-component")
+ |> render_click("toggle_oauth_clients", %{})
+
+ assert expanded_html =~ valid_attrs.name
+
saved_clients_names =
Lightning.Repo.all(OauthClient)
|> Enum.map(fn client -> client.name end)
assert valid_attrs.name in saved_clients_names
- assert Lightning.Repo.all(OauthClient)
- |> Enum.map(fn client ->
- MapSet.subset?(
- MapSet.new(String.split(client.mandatory_scopes, ",")),
- MapSet.new(added_mandatory_scopes)
- ) and
- MapSet.subset?(
- MapSet.new(String.split(client.optional_scopes, ",")),
- MapSet.new(added_optional_scopes)
- )
- end)
- |> Enum.all?()
+ new_client =
+ Lightning.Repo.all(
+ from c in OauthClient, where: c.name == ^valid_attrs.name
+ )
+ |> List.first()
+
+ assert new_client
+
+ assert MapSet.subset?(
+ MapSet.new(String.split(new_client.mandatory_scopes, ",")),
+ MapSet.new(added_mandatory_scopes)
+ )
+
+ assert MapSet.subset?(
+ MapSet.new(String.split(new_client.optional_scopes, ",")),
+ MapSet.new(added_optional_scopes)
+ )
end)
end
end