mirror of
https://github.com/livebook-dev/livebook.git
synced 2025-12-09 13:16:08 +08:00
Migrate HubLive to Phoenix format (#1373)
This commit is contained in:
parent
9b18eb54cc
commit
2cbd81eaf5
10 changed files with 433 additions and 215 deletions
|
|
@ -57,7 +57,10 @@ defmodule Livebook.Hubs.Fly do
|
|||
changeset = changeset(fly, attrs)
|
||||
|
||||
if Hubs.hub_exists?(fly.id) do
|
||||
{:error, add_error(changeset, :application_id, "already exists")}
|
||||
{:error,
|
||||
changeset
|
||||
|> add_error(:application_id, "already exists")
|
||||
|> Map.replace!(:action, :validate)}
|
||||
else
|
||||
with {:ok, struct} <- apply_action(changeset, :insert) do
|
||||
Hubs.save_hub(struct)
|
||||
|
|
@ -82,7 +85,10 @@ defmodule Livebook.Hubs.Fly do
|
|||
{:ok, struct}
|
||||
end
|
||||
else
|
||||
{:error, add_error(changeset, :application_id, "does not exists")}
|
||||
{:error,
|
||||
changeset
|
||||
|> add_error(:application_id, "does not exists")
|
||||
|> Map.replace!(:action, :validate)}
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -37,9 +37,28 @@ defmodule Livebook.Hubs.FlyClient do
|
|||
end
|
||||
end
|
||||
|
||||
defp graphql(access_token, query) do
|
||||
def fetch_app(%Fly{application_id: app_id, access_token: access_token}) do
|
||||
query = """
|
||||
query($appId: String!) {
|
||||
app(id: $appId) {
|
||||
id
|
||||
name
|
||||
hostname
|
||||
platformVersion
|
||||
deployed
|
||||
status
|
||||
}
|
||||
}
|
||||
"""
|
||||
|
||||
with {:ok, body} <- graphql(access_token, query, %{appId: app_id}) do
|
||||
{:ok, body["app"]}
|
||||
end
|
||||
end
|
||||
|
||||
defp graphql(access_token, query, input \\ %{}) do
|
||||
headers = [{"Authorization", "Bearer #{access_token}"}]
|
||||
body = {"application/json", Jason.encode!(%{query: query})}
|
||||
body = {"application/json", Jason.encode!(%{query: query, variables: input})}
|
||||
|
||||
case HTTP.request(:post, graphql_endpoint(), headers: headers, body: body) do
|
||||
{:ok, 200, _, body} ->
|
||||
|
|
|
|||
134
lib/livebook_web/live/hub/edit/fly_component.ex
Normal file
134
lib/livebook_web/live/hub/edit/fly_component.ex
Normal file
|
|
@ -0,0 +1,134 @@
|
|||
defmodule LivebookWeb.Hub.Edit.FlyComponent do
|
||||
use LivebookWeb, :live_component
|
||||
|
||||
import Ecto.Changeset, only: [get_field: 2]
|
||||
|
||||
alias Livebook.EctoTypes.HexColor
|
||||
alias Livebook.Hubs.{Fly, FlyClient}
|
||||
|
||||
@impl true
|
||||
def update(assigns, socket) do
|
||||
changeset = Fly.change_hub(assigns.hub)
|
||||
{:ok, app} = FlyClient.fetch_app(assigns.hub)
|
||||
|
||||
{:ok,
|
||||
socket
|
||||
|> assign(assigns)
|
||||
|> assign(app_url: "https://#{app["hostname"]}", changeset: changeset)}
|
||||
end
|
||||
|
||||
@impl true
|
||||
def render(assigns) do
|
||||
~H"""
|
||||
<div>
|
||||
<!-- System details -->
|
||||
<div class="flex flex-col space-y-2 pb-5">
|
||||
<div class="flex items-center justify-between border border-gray-200 rounded-lg p-4">
|
||||
<div class="flex items-center space-x-12">
|
||||
<.labeled_text label="Application ID">
|
||||
<%= @hub.application_id %>
|
||||
</.labeled_text>
|
||||
<.labeled_text label="Type">
|
||||
Fly
|
||||
</.labeled_text>
|
||||
</div>
|
||||
|
||||
<a href={@app_url} class="button-base button-outlined-gray" target="_blank">
|
||||
<.remix_icon icon="dashboard-2-line" class="align-middle mr-1" />
|
||||
<span>See app on Fly</span>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flex flex-col space-y-4">
|
||||
<h2 class="text-xl text-gray-800 font-semibold pb-2 border-b border-gray-200">
|
||||
General
|
||||
</h2>
|
||||
|
||||
<.form
|
||||
id={@id}
|
||||
class="flex flex-col mt-4 space-y-4"
|
||||
let={f}
|
||||
for={@changeset}
|
||||
phx-submit="save"
|
||||
phx-change="validate"
|
||||
phx-target={@myself}
|
||||
phx-debounce="blur"
|
||||
>
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 gap-3">
|
||||
<div class="flex flex-col space-y-1">
|
||||
<h3 class="text-gray-800 font-semibold">
|
||||
Name
|
||||
</h3>
|
||||
<%= text_input(f, :hub_name, class: "input") %>
|
||||
<%= error_tag(f, :hub_name) %>
|
||||
</div>
|
||||
|
||||
<div class="flex flex-col space-y-1">
|
||||
<h3 class="text-gray-800 font-semibold">
|
||||
Color
|
||||
</h3>
|
||||
|
||||
<div class="flex space-x-4 items-center">
|
||||
<div
|
||||
class="border-[3px] rounded-lg p-1 flex justify-center items-center"
|
||||
style={"border-color: #{hub_color(@changeset)}"}
|
||||
>
|
||||
<div class="rounded h-5 w-5" style={"background-color: #{hub_color(@changeset)}"} />
|
||||
</div>
|
||||
<div class="relative grow">
|
||||
<%= text_input(f, :hub_color,
|
||||
class: "input",
|
||||
spellcheck: "false",
|
||||
maxlength: 7
|
||||
) %>
|
||||
<button
|
||||
class="icon-button absolute right-2 top-1"
|
||||
type="button"
|
||||
phx-click="randomize_color"
|
||||
phx-target={@myself}
|
||||
>
|
||||
<.remix_icon icon="refresh-line" class="text-xl" />
|
||||
</button>
|
||||
<%= error_tag(f, :hub_color) %>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<%= submit("Update Hub",
|
||||
class: "button-base button-blue",
|
||||
phx_disable_with: "Updating...",
|
||||
disabled: not @changeset.valid?
|
||||
) %>
|
||||
</.form>
|
||||
</div>
|
||||
</div>
|
||||
"""
|
||||
end
|
||||
|
||||
@impl true
|
||||
def handle_event("randomize_color", _, socket) do
|
||||
handle_event("validate", %{"fly" => %{"hub_color" => HexColor.random()}}, socket)
|
||||
end
|
||||
|
||||
def handle_event("save", %{"fly" => params}, socket) do
|
||||
case Fly.update_hub(socket.assigns.hub, params) do
|
||||
{:ok, hub} ->
|
||||
{:noreply,
|
||||
socket
|
||||
|> put_flash(:success, "Hub updated successfully")
|
||||
|> push_redirect(to: Routes.hub_path(socket, :edit, hub.id))}
|
||||
|
||||
{:error, changeset} ->
|
||||
{:noreply, assign(socket, changeset: changeset)}
|
||||
end
|
||||
end
|
||||
|
||||
def handle_event("validate", %{"fly" => attrs}, socket) do
|
||||
changeset = Fly.change_hub(socket.assigns.hub, attrs)
|
||||
{:noreply, assign(socket, changeset: changeset)}
|
||||
end
|
||||
|
||||
defp hub_color(changeset), do: get_field(changeset, :hub_color)
|
||||
end
|
||||
44
lib/livebook_web/live/hub/edit_live.ex
Normal file
44
lib/livebook_web/live/hub/edit_live.ex
Normal file
|
|
@ -0,0 +1,44 @@
|
|||
defmodule LivebookWeb.Hub.EditLive do
|
||||
use LivebookWeb, :live_view
|
||||
|
||||
alias LivebookWeb.{PageHelpers, LayoutHelpers}
|
||||
alias Livebook.Hubs
|
||||
alias Livebook.Hubs.Provider
|
||||
|
||||
on_mount LivebookWeb.SidebarHook
|
||||
|
||||
@impl true
|
||||
def mount(%{"id" => id}, _session, socket) do
|
||||
hub = Hubs.fetch_hub!(id)
|
||||
type = Provider.type(hub)
|
||||
|
||||
if type == "local" do
|
||||
{:ok,
|
||||
socket |> redirect(to: "/") |> put_flash(:warning, "You can't edit the localhost Hub")}
|
||||
else
|
||||
{:ok, assign(socket, hub: hub, type: type, page_title: "Livebook - Hub")}
|
||||
end
|
||||
end
|
||||
|
||||
@impl true
|
||||
def render(assigns) do
|
||||
~H"""
|
||||
<LayoutHelpers.layout
|
||||
socket={@socket}
|
||||
current_page={Routes.hub_path(@socket, :edit, @hub.id)}
|
||||
current_user={@current_user}
|
||||
saved_hubs={@saved_hubs}
|
||||
>
|
||||
<div class="px-4 sm:px-8 md:px-16 pt-4 sm:py-7 max-w-screen-md mx-auto space-y-8">
|
||||
<div>
|
||||
<PageHelpers.title text="Edit Hub" socket={@socket} />
|
||||
</div>
|
||||
|
||||
<%= if @type == "fly" do %>
|
||||
<.live_component module={LivebookWeb.Hub.Edit.FlyComponent} hub={@hub} id="fly-form" />
|
||||
<% end %>
|
||||
</div>
|
||||
</LayoutHelpers.layout>
|
||||
"""
|
||||
end
|
||||
end
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
defmodule LivebookWeb.HubLive.FlyComponent do
|
||||
defmodule LivebookWeb.Hub.New.FlyComponent do
|
||||
use LivebookWeb, :live_component
|
||||
|
||||
import Ecto.Changeset, only: [get_field: 2, add_error: 3]
|
||||
|
|
@ -11,7 +11,12 @@ defmodule LivebookWeb.HubLive.FlyComponent do
|
|||
{:ok,
|
||||
socket
|
||||
|> assign(assigns)
|
||||
|> load_data()}
|
||||
|> assign(
|
||||
changeset: Fly.change_hub(%Fly{}),
|
||||
selected_app: nil,
|
||||
select_options: [],
|
||||
apps: []
|
||||
)}
|
||||
end
|
||||
|
||||
@impl true
|
||||
|
|
@ -37,7 +42,6 @@ defmodule LivebookWeb.HubLive.FlyComponent do
|
|||
phx_debounce: "blur",
|
||||
phx_target: @myself,
|
||||
value: access_token(@changeset),
|
||||
disabled: @operation == :edit,
|
||||
class: "input w-full",
|
||||
autofocus: true,
|
||||
spellcheck: "false",
|
||||
|
|
@ -51,10 +55,7 @@ defmodule LivebookWeb.HubLive.FlyComponent do
|
|||
<h3 class="text-gray-800 font-semibold">
|
||||
Application
|
||||
</h3>
|
||||
<%= select(f, :application_id, @select_options,
|
||||
class: "input",
|
||||
disabled: @operation == :edit
|
||||
) %>
|
||||
<%= select(f, :application_id, @select_options, class: "input") %>
|
||||
<%= error_tag(f, :application_id) %>
|
||||
</div>
|
||||
|
||||
|
|
@ -99,10 +100,10 @@ defmodule LivebookWeb.HubLive.FlyComponent do
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<%= submit("Save",
|
||||
<%= submit("Add Hub",
|
||||
class: "button-base button-blue",
|
||||
phx_disable_with: "Saving...",
|
||||
disabled: not @valid?
|
||||
phx_disable_with: "Add...",
|
||||
disabled: not @changeset.valid?
|
||||
) %>
|
||||
<% end %>
|
||||
</.form>
|
||||
|
|
@ -110,29 +111,6 @@ defmodule LivebookWeb.HubLive.FlyComponent do
|
|||
"""
|
||||
end
|
||||
|
||||
defp load_data(%{assigns: %{operation: :new}} = socket) do
|
||||
assign(socket,
|
||||
changeset: Fly.change_hub(%Fly{}),
|
||||
selected_app: nil,
|
||||
select_options: [],
|
||||
apps: [],
|
||||
valid?: false
|
||||
)
|
||||
end
|
||||
|
||||
defp load_data(%{assigns: %{operation: :edit, hub: hub}} = socket) do
|
||||
{:ok, apps} = FlyClient.fetch_apps(hub.access_token)
|
||||
params = Map.from_struct(hub)
|
||||
|
||||
assign(socket,
|
||||
changeset: Fly.change_hub(hub, params),
|
||||
selected_app: hub,
|
||||
select_options: select_options(apps),
|
||||
apps: apps,
|
||||
valid?: true
|
||||
)
|
||||
end
|
||||
|
||||
@impl true
|
||||
def handle_event("fetch_data", %{"fly" => %{"access_token" => token}}, socket) do
|
||||
case FlyClient.fetch_apps(token) do
|
||||
|
|
@ -140,13 +118,7 @@ defmodule LivebookWeb.HubLive.FlyComponent do
|
|||
opts = select_options(apps)
|
||||
changeset = Fly.change_hub(%Fly{}, %{access_token: token, hub_color: HexColor.random()})
|
||||
|
||||
{:noreply,
|
||||
assign(socket,
|
||||
changeset: changeset,
|
||||
valid?: changeset.valid?,
|
||||
select_options: opts,
|
||||
apps: apps
|
||||
)}
|
||||
{:noreply, assign(socket, changeset: changeset, select_options: opts, apps: apps)}
|
||||
|
||||
{:error, _} ->
|
||||
changeset =
|
||||
|
|
@ -154,13 +126,7 @@ defmodule LivebookWeb.HubLive.FlyComponent do
|
|||
|> Fly.change_hub(%{access_token: token})
|
||||
|> add_error(:access_token, "is invalid")
|
||||
|
||||
{:noreply,
|
||||
assign(socket,
|
||||
changeset: changeset,
|
||||
valid?: changeset.valid?,
|
||||
select_options: [],
|
||||
apps: []
|
||||
)}
|
||||
{:noreply, assign(socket, changeset: changeset, select_options: [], apps: [])}
|
||||
end
|
||||
end
|
||||
|
||||
|
|
@ -169,8 +135,17 @@ defmodule LivebookWeb.HubLive.FlyComponent do
|
|||
end
|
||||
|
||||
def handle_event("save", %{"fly" => params}, socket) do
|
||||
if socket.assigns.valid? do
|
||||
{:noreply, save_fly(socket, socket.assigns.operation, params)}
|
||||
if socket.assigns.changeset.valid? do
|
||||
case Fly.create_hub(socket.assigns.selected_app, params) do
|
||||
{:ok, hub} ->
|
||||
{:noreply,
|
||||
socket
|
||||
|> put_flash(:success, "Hub added successfully")
|
||||
|> push_redirect(to: Routes.hub_path(socket, :edit, hub.id))}
|
||||
|
||||
{:error, changeset} ->
|
||||
{:noreply, assign(socket, changeset: changeset)}
|
||||
end
|
||||
else
|
||||
{:noreply, socket}
|
||||
end
|
||||
|
|
@ -193,12 +168,7 @@ defmodule LivebookWeb.HubLive.FlyComponent do
|
|||
end
|
||||
|
||||
{:noreply,
|
||||
assign(socket,
|
||||
changeset: changeset,
|
||||
valid?: changeset.valid?,
|
||||
selected_app: selected_app,
|
||||
select_options: opts
|
||||
)}
|
||||
assign(socket, changeset: changeset, selected_app: selected_app, select_options: opts)}
|
||||
end
|
||||
|
||||
defp select_options(hubs, app_id \\ nil) do
|
||||
|
|
@ -216,38 +186,6 @@ defmodule LivebookWeb.HubLive.FlyComponent do
|
|||
[disabled_option] ++ options
|
||||
end
|
||||
|
||||
defp save_fly(socket, :new, params) do
|
||||
case Fly.create_hub(socket.assigns.selected_app, params) do
|
||||
{:ok, hub} ->
|
||||
changeset = Fly.change_hub(hub, params)
|
||||
|
||||
socket
|
||||
|> assign(changeset: changeset, selected_app: hub, valid?: changeset.valid?)
|
||||
|> put_flash(:success, "Hub created successfully")
|
||||
|> push_redirect(to: Routes.hub_path(socket, :edit, hub.id))
|
||||
|
||||
{:error, changeset} ->
|
||||
assign(socket, changeset: %{changeset | action: :validate}, valid?: changeset.valid?)
|
||||
end
|
||||
end
|
||||
|
||||
defp save_fly(socket, :edit, params) do
|
||||
id = socket.assigns.selected_app.id
|
||||
|
||||
case Fly.update_hub(socket.assigns.selected_app, params) do
|
||||
{:ok, hub} ->
|
||||
changeset = Fly.change_hub(hub, params)
|
||||
|
||||
socket
|
||||
|> assign(changeset: changeset, selected_app: hub, valid?: changeset.valid?)
|
||||
|> put_flash(:success, "Hub updated successfully")
|
||||
|> push_redirect(to: Routes.hub_path(socket, :edit, id))
|
||||
|
||||
{:error, changeset} ->
|
||||
assign(socket, changeset: %{changeset | action: :validate}, valid?: changeset.valid?)
|
||||
end
|
||||
end
|
||||
|
||||
defp hub_color(changeset), do: get_field(changeset, :hub_color)
|
||||
defp access_token(changeset), do: get_field(changeset, :access_token)
|
||||
end
|
||||
|
|
@ -1,8 +1,6 @@
|
|||
defmodule LivebookWeb.HubLive do
|
||||
defmodule LivebookWeb.Hub.NewLive do
|
||||
use LivebookWeb, :live_view
|
||||
|
||||
alias Livebook.Hubs
|
||||
alias Livebook.Hubs.Provider
|
||||
alias LivebookWeb.{PageHelpers, LayoutHelpers}
|
||||
alias Phoenix.LiveView.JS
|
||||
|
||||
|
|
@ -10,12 +8,7 @@ defmodule LivebookWeb.HubLive do
|
|||
|
||||
@impl true
|
||||
def mount(_params, _session, socket) do
|
||||
{:ok,
|
||||
assign(socket,
|
||||
selected_provider: nil,
|
||||
hub: nil,
|
||||
page_title: "Livebook - Hub"
|
||||
)}
|
||||
{:ok, assign(socket, selected_type: nil, page_title: "Livebook - Hub")}
|
||||
end
|
||||
|
||||
@impl true
|
||||
|
|
@ -23,16 +16,13 @@ defmodule LivebookWeb.HubLive do
|
|||
~H"""
|
||||
<LayoutHelpers.layout
|
||||
socket={@socket}
|
||||
current_page={@current_page}
|
||||
current_page={Routes.hub_path(@socket, :new)}
|
||||
current_user={@current_user}
|
||||
saved_hubs={@saved_hubs}
|
||||
>
|
||||
<div class="px-4 sm:px-8 md:px-16 pt-4 sm:py-7 max-w-screen-md mx-auto space-y-8">
|
||||
<div>
|
||||
<PageHelpers.title
|
||||
text={if @operation == :new, do: "Add Hub", else: "Edit Hub"}
|
||||
socket={@socket}
|
||||
/>
|
||||
<PageHelpers.title text="Add Hub" socket={@socket} />
|
||||
<p class="mt-4 text-gray-700">
|
||||
Manage your Livebooks in the cloud with Hubs.
|
||||
</p>
|
||||
|
|
@ -44,7 +34,7 @@ defmodule LivebookWeb.HubLive do
|
|||
</h2>
|
||||
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
|
||||
<.card_item id="fly" selected={@selected_provider} title="Fly">
|
||||
<.card_item id="fly" selected={@selected_type} title="Fly">
|
||||
<:logo>
|
||||
<%= Phoenix.HTML.raw(File.read!("static/images/fly.svg")) %>
|
||||
</:logo>
|
||||
|
|
@ -53,7 +43,7 @@ defmodule LivebookWeb.HubLive do
|
|||
</:headline>
|
||||
</.card_item>
|
||||
|
||||
<.card_item id="enterprise" selected={@selected_provider} title="Livebook Enterprise">
|
||||
<.card_item id="enterprise" selected={@selected_type} title="Livebook Enterprise">
|
||||
<:logo>
|
||||
<img
|
||||
src="/images/enterprise.png"
|
||||
|
|
@ -68,22 +58,17 @@ defmodule LivebookWeb.HubLive do
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<%= if @selected_provider do %>
|
||||
<%= if @selected_type do %>
|
||||
<div class="flex flex-col space-y-4">
|
||||
<h2 class="text-xl text-gray-800 font-semibold pb-2 border-b border-gray-200">
|
||||
2. Configure your Hub
|
||||
</h2>
|
||||
|
||||
<%= if @selected_provider == "fly" do %>
|
||||
<.live_component
|
||||
module={LivebookWeb.HubLive.FlyComponent}
|
||||
id="fly-form"
|
||||
operation={@operation}
|
||||
hub={@hub}
|
||||
/>
|
||||
<%= if @selected_type == "fly" do %>
|
||||
<.live_component module={LivebookWeb.Hub.New.FlyComponent} id="fly-form" />
|
||||
<% end %>
|
||||
|
||||
<%= if @selected_provider == "enterprise" do %>
|
||||
<%= if @selected_type == "enterprise" do %>
|
||||
<div>
|
||||
Livebook Enterprise is currently in closed beta. If you want to learn more, <a
|
||||
href="https://livebook.dev/#livebook-plans"
|
||||
|
|
@ -104,7 +89,7 @@ defmodule LivebookWeb.HubLive do
|
|||
<div
|
||||
id={@id}
|
||||
class={"flex card-item flex-col " <> card_item_bg_color(@id, @selected)}
|
||||
phx-click={JS.push("select_provider", value: %{value: @id})}
|
||||
phx-click={JS.push("select_type", value: %{value: @id})}
|
||||
>
|
||||
<div class="flex items-center justify-center card-item-logo p-6 border-2 rounded-t-2xl h-[150px]">
|
||||
<%= render_slot(@logo) %>
|
||||
|
|
@ -126,30 +111,7 @@ defmodule LivebookWeb.HubLive do
|
|||
defp card_item_bg_color(_id, _selected), do: ""
|
||||
|
||||
@impl true
|
||||
def handle_params(%{"id" => "local-host"}, _url, socket) do
|
||||
{:noreply,
|
||||
socket |> redirect(to: "/") |> put_flash(:warning, "You can't edit the localhost Hub")}
|
||||
end
|
||||
|
||||
def handle_params(%{"id" => id}, _url, socket) do
|
||||
hub = Hubs.fetch_hub!(id)
|
||||
provider = Provider.type(hub)
|
||||
|
||||
{:noreply,
|
||||
assign(socket,
|
||||
operation: :edit,
|
||||
hub: hub,
|
||||
selected_provider: provider,
|
||||
current_page: Routes.hub_path(socket, :edit, hub.id)
|
||||
)}
|
||||
end
|
||||
|
||||
def handle_params(_params, _url, socket) do
|
||||
{:noreply, assign(socket, operation: :new, current_page: Routes.hub_path(socket, :new))}
|
||||
end
|
||||
|
||||
@impl true
|
||||
def handle_event("select_provider", %{"value" => service}, socket) do
|
||||
{:noreply, assign(socket, selected_provider: service)}
|
||||
def handle_event("select_type", %{"value" => service}, socket) do
|
||||
{:noreply, assign(socket, selected_type: service)}
|
||||
end
|
||||
end
|
||||
|
|
@ -55,8 +55,8 @@ defmodule LivebookWeb.Router do
|
|||
live "/explore", ExploreLive, :page
|
||||
live "/explore/notebooks/:slug", ExploreLive, :notebook
|
||||
|
||||
live "/hub", HubLive, :new
|
||||
live "/hub/:id", HubLive, :edit
|
||||
live "/hub", Hub.NewLive, :new, as: :hub
|
||||
live "/hub/:id", Hub.EditLive, :edit, as: :hub
|
||||
|
||||
live "/sessions/:id", SessionLive, :page
|
||||
live "/sessions/:id/shortcuts", SessionLive, :shortcuts
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
defmodule Livebook.Hubs.FlyClientTest do
|
||||
use ExUnit.Case
|
||||
use Livebook.DataCase
|
||||
|
||||
alias Livebook.Hubs.{Fly, FlyClient}
|
||||
|
||||
|
|
@ -68,4 +68,42 @@ defmodule Livebook.Hubs.FlyClientTest do
|
|||
assert {:error, "request failed with code: UNAUTHORIZED"} = FlyClient.fetch_apps("foo")
|
||||
end
|
||||
end
|
||||
|
||||
describe "fetch_app/1" do
|
||||
test "fetches an application", %{bypass: bypass} do
|
||||
app = %{
|
||||
"id" => "foo-app",
|
||||
"name" => "foo-app",
|
||||
"hostname" => "foo-app.fly.dev",
|
||||
"platformVersion" => "nomad",
|
||||
"deployed" => true,
|
||||
"status" => "running"
|
||||
}
|
||||
|
||||
response = %{"data" => %{"app" => app}}
|
||||
|
||||
Bypass.expect_once(bypass, "POST", "/", fn conn ->
|
||||
conn
|
||||
|> Plug.Conn.put_resp_content_type("application/json")
|
||||
|> Plug.Conn.resp(200, Jason.encode!(response))
|
||||
end)
|
||||
|
||||
hub = build(:fly)
|
||||
assert {:ok, ^app} = FlyClient.fetch_app(hub)
|
||||
end
|
||||
|
||||
test "returns unauthorized when token is invalid", %{bypass: bypass} do
|
||||
error = %{"extensions" => %{"code" => "UNAUTHORIZED"}}
|
||||
response = %{"data" => nil, "errors" => [error]}
|
||||
|
||||
Bypass.expect_once(bypass, "POST", "/", fn conn ->
|
||||
conn
|
||||
|> Plug.Conn.put_resp_content_type("application/json")
|
||||
|> Plug.Conn.resp(200, Jason.encode!(response))
|
||||
end)
|
||||
|
||||
hub = build(:fly)
|
||||
assert {:error, "request failed with code: UNAUTHORIZED"} = FlyClient.fetch_app(hub)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
104
test/livebook_web/live/hub/edit_live_test.exs
Normal file
104
test/livebook_web/live/hub/edit_live_test.exs
Normal file
|
|
@ -0,0 +1,104 @@
|
|||
defmodule LivebookWeb.Hub.EditLiveTest do
|
||||
use LivebookWeb.ConnCase
|
||||
|
||||
import Phoenix.LiveViewTest
|
||||
|
||||
alias Livebook.Hubs
|
||||
|
||||
setup do
|
||||
on_exit(&Hubs.clean_hubs/0)
|
||||
:ok
|
||||
end
|
||||
|
||||
describe "fly" do
|
||||
test "updates fly", %{conn: conn} do
|
||||
hub = insert_hub(:fly, id: "fly-987654321", application_id: "987654321")
|
||||
fly_bypass(hub.application_id)
|
||||
|
||||
{:ok, view, html} = live(conn, Routes.hub_path(conn, :edit, hub.id))
|
||||
|
||||
assert html =~ "See app on Fly"
|
||||
assert html =~ "https://#{hub.application_id}.fly.dev"
|
||||
|
||||
attrs = %{
|
||||
"hub_name" => "Personal Hub",
|
||||
"hub_color" => "#FF00FF"
|
||||
}
|
||||
|
||||
view
|
||||
|> element("#fly-form")
|
||||
|> render_change(%{"fly" => attrs})
|
||||
|
||||
refute view
|
||||
|> element("#fly-form .invalid-feedback")
|
||||
|> has_element?()
|
||||
|
||||
assert {:ok, view, _html} =
|
||||
view
|
||||
|> element("#fly-form")
|
||||
|> render_submit(%{"fly" => attrs})
|
||||
|> follow_redirect(conn)
|
||||
|
||||
assert render(view) =~ "Hub updated successfully"
|
||||
|
||||
assert view
|
||||
|> element("#hubs")
|
||||
|> render() =~ ~s/style="color: #FF00FF"/
|
||||
|
||||
assert view
|
||||
|> element("#hubs")
|
||||
|> render() =~ "/hub/fly-987654321"
|
||||
|
||||
assert view
|
||||
|> element("#hubs")
|
||||
|> render() =~ "Personal Hub"
|
||||
|
||||
refute Hubs.fetch_hub!(hub.id) == hub
|
||||
end
|
||||
end
|
||||
|
||||
defp fly_bypass(app_id) do
|
||||
bypass = Bypass.open()
|
||||
Application.put_env(:livebook, :fly_graphql_endpoint, "http://localhost:#{bypass.port}")
|
||||
|
||||
Bypass.expect(bypass, "POST", "/", fn conn ->
|
||||
{:ok, body, conn} = Plug.Conn.read_body(conn)
|
||||
|
||||
response =
|
||||
case Jason.decode!(body) do
|
||||
%{"variables" => %{"appId" => ^app_id}} -> fetch_app_response(app_id)
|
||||
%{"variables" => %{}} -> fetch_apps_response(app_id)
|
||||
end
|
||||
|
||||
conn
|
||||
|> Plug.Conn.put_resp_content_type("application/json")
|
||||
|> Plug.Conn.resp(200, Jason.encode!(response))
|
||||
end)
|
||||
end
|
||||
|
||||
defp fetch_apps_response(app_id) do
|
||||
app = %{
|
||||
"id" => app_id,
|
||||
"organization" => %{
|
||||
"id" => "l3soyvjmvtmwtl6l2drnbfuvltipprge",
|
||||
"name" => "Foo Bar",
|
||||
"type" => "PERSONAL"
|
||||
}
|
||||
}
|
||||
|
||||
%{"data" => %{"apps" => %{"nodes" => [app]}}}
|
||||
end
|
||||
|
||||
defp fetch_app_response(app_id) do
|
||||
app = %{
|
||||
"id" => app_id,
|
||||
"name" => app_id,
|
||||
"hostname" => app_id <> ".fly.dev",
|
||||
"platformVersion" => "nomad",
|
||||
"deployed" => true,
|
||||
"status" => "running"
|
||||
}
|
||||
|
||||
%{"data" => %{"app" => app}}
|
||||
end
|
||||
end
|
||||
|
|
@ -1,5 +1,5 @@
|
|||
defmodule LivebookWeb.HubLiveTest do
|
||||
use LivebookWeb.ConnCase, async: true
|
||||
defmodule LivebookWeb.Hub.NewLiveTest do
|
||||
use LivebookWeb.ConnCase
|
||||
|
||||
import Phoenix.LiveViewTest
|
||||
|
||||
|
|
@ -7,12 +7,11 @@ defmodule LivebookWeb.HubLiveTest do
|
|||
|
||||
setup do
|
||||
on_exit(&Hubs.clean_hubs/0)
|
||||
|
||||
:ok
|
||||
end
|
||||
|
||||
test "render hub selection cards", %{conn: conn} do
|
||||
{:ok, _view, html} = live(conn, "/hub")
|
||||
{:ok, _view, html} = live(conn, Routes.hub_path(conn, :new))
|
||||
|
||||
assert html =~ "Fly"
|
||||
assert html =~ "Livebook Enterprise"
|
||||
|
|
@ -20,9 +19,9 @@ defmodule LivebookWeb.HubLiveTest do
|
|||
|
||||
describe "fly" do
|
||||
test "persists fly", %{conn: conn} do
|
||||
fly_app_bypass("123456789")
|
||||
fly_bypass("123456789")
|
||||
|
||||
{:ok, view, _html} = live(conn, "/hub")
|
||||
{:ok, view, _html} = live(conn, Routes.hub_path(conn, :new))
|
||||
|
||||
assert view
|
||||
|> element("#fly")
|
||||
|
|
@ -54,7 +53,7 @@ defmodule LivebookWeb.HubLiveTest do
|
|||
|> render_submit(%{"fly" => attrs})
|
||||
|> follow_redirect(conn)
|
||||
|
||||
assert render(view) =~ "Hub created successfully"
|
||||
assert render(view) =~ "Hub added successfully"
|
||||
|
||||
assert view
|
||||
|> element("#hubs")
|
||||
|
|
@ -69,60 +68,11 @@ defmodule LivebookWeb.HubLiveTest do
|
|||
|> render() =~ "My Foo Hub"
|
||||
end
|
||||
|
||||
test "updates fly", %{conn: conn} do
|
||||
fly_app_bypass("987654321")
|
||||
fly = insert_hub(:fly, id: "fly-987654321", application_id: "987654321")
|
||||
|
||||
{:ok, view, _html} = live(conn, "/hub/fly-987654321")
|
||||
|
||||
assert render(view) =~ "2. Configure your Hub"
|
||||
|
||||
assert render(view) =~
|
||||
~s(<option selected="selected" value="987654321">Foo Bar - 987654321</option>)
|
||||
|
||||
attrs = %{
|
||||
"access_token" => "dummy access token",
|
||||
"application_id" => "987654321",
|
||||
"hub_name" => "Personal Hub",
|
||||
"hub_color" => "#FF00FF"
|
||||
}
|
||||
|
||||
view
|
||||
|> element("#fly-form")
|
||||
|> render_change(%{"fly" => attrs})
|
||||
|
||||
refute view
|
||||
|> element("#fly-form .invalid-feedback")
|
||||
|> has_element?()
|
||||
|
||||
assert {:ok, view, _html} =
|
||||
view
|
||||
|> element("#fly-form")
|
||||
|> render_submit(%{"fly" => attrs})
|
||||
|> follow_redirect(conn)
|
||||
|
||||
assert render(view) =~ "Hub updated successfully"
|
||||
|
||||
assert view
|
||||
|> element("#hubs")
|
||||
|> render() =~ ~s/style="color: #FF00FF"/
|
||||
|
||||
assert view
|
||||
|> element("#hubs")
|
||||
|> render() =~ "/hub/fly-987654321"
|
||||
|
||||
assert view
|
||||
|> element("#hubs")
|
||||
|> render() =~ "Personal Hub"
|
||||
|
||||
refute Hubs.fetch_hub!("fly-987654321") == fly
|
||||
end
|
||||
|
||||
test "fails to create existing hub", %{conn: conn} do
|
||||
fly = insert_hub(:fly, id: "fly-foo", application_id: "foo")
|
||||
fly_app_bypass("foo")
|
||||
hub = insert_hub(:fly, id: "fly-foo", application_id: "foo")
|
||||
fly_bypass(hub.application_id)
|
||||
|
||||
{:ok, view, _html} = live(conn, "/hub")
|
||||
{:ok, view, _html} = live(conn, Routes.hub_path(conn, :new))
|
||||
|
||||
assert view
|
||||
|> element("#fly")
|
||||
|
|
@ -154,24 +104,40 @@ defmodule LivebookWeb.HubLiveTest do
|
|||
|
||||
assert view
|
||||
|> element("#hubs")
|
||||
|> render() =~ ~s/style="color: #{fly.hub_color}"/
|
||||
|> render() =~ ~s/style="color: #{hub.hub_color}"/
|
||||
|
||||
assert view
|
||||
|> element("#hubs")
|
||||
|> render() =~ "/hub/fly-foo"
|
||||
|> render() =~ Routes.hub_path(conn, :edit, hub.id)
|
||||
|
||||
assert view
|
||||
|> element("#hubs")
|
||||
|> render() =~ fly.hub_name
|
||||
|> render() =~ hub.hub_name
|
||||
|
||||
assert Hubs.fetch_hub!("fly-foo") == fly
|
||||
assert Hubs.fetch_hub!(hub.id) == hub
|
||||
end
|
||||
end
|
||||
|
||||
defp fly_app_bypass(app_id) do
|
||||
defp fly_bypass(app_id) do
|
||||
bypass = Bypass.open()
|
||||
Application.put_env(:livebook, :fly_graphql_endpoint, "http://localhost:#{bypass.port}")
|
||||
|
||||
Bypass.expect(bypass, "POST", "/", fn conn ->
|
||||
{:ok, body, conn} = Plug.Conn.read_body(conn)
|
||||
|
||||
response =
|
||||
case Jason.decode!(body) do
|
||||
%{"variables" => %{"appId" => ^app_id}} -> fetch_app_response(app_id)
|
||||
%{"variables" => %{}} -> fetch_apps_response(app_id)
|
||||
end
|
||||
|
||||
conn
|
||||
|> Plug.Conn.put_resp_content_type("application/json")
|
||||
|> Plug.Conn.resp(200, Jason.encode!(response))
|
||||
end)
|
||||
end
|
||||
|
||||
defp fetch_apps_response(app_id) do
|
||||
app = %{
|
||||
"id" => app_id,
|
||||
"organization" => %{
|
||||
|
|
@ -181,12 +147,19 @@ defmodule LivebookWeb.HubLiveTest do
|
|||
}
|
||||
}
|
||||
|
||||
response = %{"data" => %{"apps" => %{"nodes" => [app]}}}
|
||||
%{"data" => %{"apps" => %{"nodes" => [app]}}}
|
||||
end
|
||||
|
||||
Bypass.expect(bypass, "POST", "/", fn conn ->
|
||||
conn
|
||||
|> Plug.Conn.put_resp_content_type("application/json")
|
||||
|> Plug.Conn.resp(200, Jason.encode!(response))
|
||||
end)
|
||||
defp fetch_app_response(app_id) do
|
||||
app = %{
|
||||
"id" => app_id,
|
||||
"name" => app_id,
|
||||
"hostname" => app_id <> ".fly.dev",
|
||||
"platformVersion" => "nomad",
|
||||
"deployed" => true,
|
||||
"status" => "running"
|
||||
}
|
||||
|
||||
%{"data" => %{"app" => app}}
|
||||
end
|
||||
end
|
||||
Loading…
Add table
Reference in a new issue