mirror of
https://github.com/livebook-dev/livebook.git
synced 2025-10-04 02:34:37 +08:00
Implement emojis instead of colors (#1636)
This commit is contained in:
parent
57e1df74f3
commit
ae3a5661a8
30 changed files with 409 additions and 228 deletions
33
assets/js/hooks/emoji_picker.js
Normal file
33
assets/js/hooks/emoji_picker.js
Normal file
|
@ -0,0 +1,33 @@
|
|||
import { createPicker } from "picmo";
|
||||
|
||||
/**
|
||||
* A hook for the emoji picker input.
|
||||
*/
|
||||
const EmojiPicker = {
|
||||
mounted() {
|
||||
const rootElement = this.el.querySelector("[data-emoji-container]");
|
||||
const preview = this.el.querySelector("[data-emoji-preview]");
|
||||
const input = this.el.querySelector("[data-emoji-input]");
|
||||
const button = this.el.querySelector("[data-emoji-button]");
|
||||
|
||||
const pickerOptions = {
|
||||
rootElement,
|
||||
showSearch: false,
|
||||
showPreview: false,
|
||||
};
|
||||
|
||||
const picker = createPicker(pickerOptions);
|
||||
|
||||
picker.addEventListener("emoji:select", ({ emoji }) => {
|
||||
preview.innerHTML = emoji;
|
||||
input.value = emoji;
|
||||
rootElement.classList.toggle("hidden");
|
||||
});
|
||||
|
||||
button.addEventListener("click", (_) => {
|
||||
rootElement.classList.toggle("hidden");
|
||||
});
|
||||
},
|
||||
};
|
||||
|
||||
export default EmojiPicker;
|
|
@ -4,6 +4,7 @@ import CellEditor from "./cell_editor";
|
|||
import ConfirmModal from "./confirm_modal";
|
||||
import Dropzone from "./dropzone";
|
||||
import EditorSettings from "./editor_settings";
|
||||
import EmojiPicker from "./emoji_picker";
|
||||
import FocusOnUpdate from "./focus_on_update";
|
||||
import Headline from "./headline";
|
||||
import Highlight from "./highlight";
|
||||
|
@ -26,6 +27,7 @@ export default {
|
|||
ConfirmModal,
|
||||
Dropzone,
|
||||
EditorSettings,
|
||||
EmojiPicker,
|
||||
FocusOnUpdate,
|
||||
Headline,
|
||||
Highlight,
|
||||
|
|
34
assets/package-lock.json
generated
34
assets/package-lock.json
generated
|
@ -21,6 +21,7 @@
|
|||
"phoenix": "file:../deps/phoenix",
|
||||
"phoenix_html": "file:../deps/phoenix_html",
|
||||
"phoenix_live_view": "file:../deps/phoenix_live_view",
|
||||
"picmo": "^5.7.2",
|
||||
"postcss-import": "^15.0.0",
|
||||
"postcss-loader": "^7.0.1",
|
||||
"rehype-katex": "^6.0.0",
|
||||
|
@ -4505,6 +4506,15 @@
|
|||
"resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
|
||||
"integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="
|
||||
},
|
||||
"node_modules/emojibase": {
|
||||
"version": "6.1.0",
|
||||
"resolved": "https://registry.npmjs.org/emojibase/-/emojibase-6.1.0.tgz",
|
||||
"integrity": "sha512-1GkKJPXP6tVkYJHOBSJHoGOr/6uaDxZ9xJ6H7m6PfdGXTmQgbALHLWaVRY4Gi/qf5x/gT/NUXLPuSHYLqtLtrQ==",
|
||||
"funding": {
|
||||
"type": "ko-fi",
|
||||
"url": "https://ko-fi.com/milesjohnson"
|
||||
}
|
||||
},
|
||||
"node_modules/emojis-list": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/emojis-list/-/emojis-list-3.0.0.tgz",
|
||||
|
@ -8544,6 +8554,17 @@
|
|||
"resolved": "../deps/phoenix_live_view",
|
||||
"link": true
|
||||
},
|
||||
"node_modules/picmo": {
|
||||
"version": "5.7.2",
|
||||
"resolved": "https://registry.npmjs.org/picmo/-/picmo-5.7.2.tgz",
|
||||
"integrity": "sha512-A7c5O8x1Xwq11KBYFY93+GIbHnw9PVz35HaWWHn/dgT08GA67M6cXKjjwzLnEAyXSdxXKrEk8/gPyTs+ibzWfQ==",
|
||||
"dependencies": {
|
||||
"emojibase": "^6.1.0"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/joeattardi"
|
||||
}
|
||||
},
|
||||
"node_modules/picocolors": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz",
|
||||
|
@ -14080,6 +14101,11 @@
|
|||
"resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
|
||||
"integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="
|
||||
},
|
||||
"emojibase": {
|
||||
"version": "6.1.0",
|
||||
"resolved": "https://registry.npmjs.org/emojibase/-/emojibase-6.1.0.tgz",
|
||||
"integrity": "sha512-1GkKJPXP6tVkYJHOBSJHoGOr/6uaDxZ9xJ6H7m6PfdGXTmQgbALHLWaVRY4Gi/qf5x/gT/NUXLPuSHYLqtLtrQ=="
|
||||
},
|
||||
"emojis-list": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/emojis-list/-/emojis-list-3.0.0.tgz",
|
||||
|
@ -16897,6 +16923,14 @@
|
|||
"phoenix_live_view": {
|
||||
"version": "file:../deps/phoenix_live_view"
|
||||
},
|
||||
"picmo": {
|
||||
"version": "5.7.2",
|
||||
"resolved": "https://registry.npmjs.org/picmo/-/picmo-5.7.2.tgz",
|
||||
"integrity": "sha512-A7c5O8x1Xwq11KBYFY93+GIbHnw9PVz35HaWWHn/dgT08GA67M6cXKjjwzLnEAyXSdxXKrEk8/gPyTs+ibzWfQ==",
|
||||
"requires": {
|
||||
"emojibase": "^6.1.0"
|
||||
}
|
||||
},
|
||||
"picocolors": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz",
|
||||
|
|
|
@ -25,6 +25,7 @@
|
|||
"phoenix": "file:../deps/phoenix",
|
||||
"phoenix_html": "file:../deps/phoenix_html",
|
||||
"phoenix_live_view": "file:../deps/phoenix_live_view",
|
||||
"picmo": "^5.7.2",
|
||||
"postcss-import": "^15.0.0",
|
||||
"postcss-loader": "^7.0.1",
|
||||
"rehype-katex": "^6.0.0",
|
||||
|
|
|
@ -197,13 +197,11 @@ defmodule Livebook.Application do
|
|||
|
||||
if Livebook.Config.feature_flag_enabled?(:localhost_hub) do
|
||||
defp insert_development_hub do
|
||||
unless Livebook.Hubs.hub_exists?("local-host") do
|
||||
Livebook.Hubs.save_hub(%Livebook.Hubs.Local{
|
||||
id: "local-host",
|
||||
hub_name: "Localhost",
|
||||
hub_color: Livebook.EctoTypes.HexColor.random()
|
||||
})
|
||||
end
|
||||
Livebook.Hubs.save_hub(%Livebook.Hubs.Local{
|
||||
id: "local-host",
|
||||
hub_name: "Localhost",
|
||||
hub_emoji: "🏠"
|
||||
})
|
||||
end
|
||||
else
|
||||
defp insert_development_hub, do: :ok
|
||||
|
|
|
@ -2,7 +2,7 @@ defmodule Livebook.Hubs do
|
|||
@moduledoc false
|
||||
|
||||
alias Livebook.Storage
|
||||
alias Livebook.Hubs.{Enterprise, Fly, Local, Metadata, Provider}
|
||||
alias Livebook.Hubs.{Broadcasts, Enterprise, Fly, Local, Metadata, Provider}
|
||||
|
||||
@namespace :hubs
|
||||
|
||||
|
@ -28,7 +28,7 @@ defmodule Livebook.Hubs do
|
|||
@spec get_metadatas() :: list(Metadata.t())
|
||||
def get_metadatas do
|
||||
for hub <- get_hubs() do
|
||||
Provider.normalize(hub)
|
||||
%{Provider.normalize(hub) | connected?: Provider.connected?(hub)}
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -71,7 +71,7 @@ defmodule Livebook.Hubs do
|
|||
attributes = struct |> Map.from_struct() |> Map.to_list()
|
||||
:ok = Storage.insert(@namespace, struct.id, attributes)
|
||||
:ok = connect_hub(struct)
|
||||
:ok = broadcast_hubs_change()
|
||||
:ok = Broadcasts.hubs_metadata_changed()
|
||||
|
||||
struct
|
||||
end
|
||||
|
@ -84,7 +84,7 @@ defmodule Livebook.Hubs do
|
|||
end
|
||||
|
||||
:ok = Storage.delete(@namespace, id)
|
||||
:ok = broadcast_hubs_change()
|
||||
:ok = Broadcasts.hubs_metadata_changed()
|
||||
end
|
||||
|
||||
:ok
|
||||
|
@ -98,30 +98,50 @@ defmodule Livebook.Hubs do
|
|||
end
|
||||
|
||||
@doc """
|
||||
Subscribes to updates in hubs information.
|
||||
Subscribes to one or more subtopics in `"hubs"`.
|
||||
|
||||
## Messages
|
||||
|
||||
* `{:hubs_metadata_changed, hubs}`
|
||||
Topic `hubs:crud`:
|
||||
|
||||
* `:hubs_metadata_changed`
|
||||
|
||||
Topic `hubs:connection`:
|
||||
|
||||
* `:hub_connected`
|
||||
* `:hub_disconnected`
|
||||
* `{:connection_error, reason}`
|
||||
* `{:disconnection_error, reason}`
|
||||
|
||||
Topic `hubs:secrets`:
|
||||
|
||||
* `{:secret_created, %Secret{}}`
|
||||
* `{:secret_updated, %Secret{}}`
|
||||
|
||||
"""
|
||||
@spec subscribe() :: :ok | {:error, term()}
|
||||
def subscribe do
|
||||
Phoenix.PubSub.subscribe(Livebook.PubSub, "hubs")
|
||||
@spec subscribe(atom() | list(atom())) :: :ok | {:error, term()}
|
||||
def subscribe(topics) when is_list(topics) do
|
||||
for topic <- topics, do: subscribe(topic)
|
||||
|
||||
:ok
|
||||
end
|
||||
|
||||
def subscribe(topic) do
|
||||
Phoenix.PubSub.subscribe(Livebook.PubSub, "hubs:#{topic}")
|
||||
end
|
||||
|
||||
@doc """
|
||||
Unsubscribes from `subscribe/0`.
|
||||
"""
|
||||
@spec unsubscribe() :: :ok
|
||||
def unsubscribe do
|
||||
Phoenix.PubSub.unsubscribe(Livebook.PubSub, "hubs")
|
||||
@spec unsubscribe(atom() | list(atom())) :: :ok
|
||||
def unsubscribe(topics) when is_list(topics) do
|
||||
for topic <- topics, do: unsubscribe(topic)
|
||||
|
||||
:ok
|
||||
end
|
||||
|
||||
# Notifies interested processes about hubs data change.
|
||||
# Broadcasts `{:hubs_metadata_changed, hubs}` message under the `"hubs"` topic.
|
||||
defp broadcast_hubs_change do
|
||||
Phoenix.PubSub.broadcast(Livebook.PubSub, "hubs", {:hubs_metadata_changed, get_metadatas()})
|
||||
def unsubscribe(topic) do
|
||||
Phoenix.PubSub.unsubscribe(Livebook.PubSub, "hubs:#{topic}")
|
||||
end
|
||||
|
||||
defp to_struct(%{id: "fly-" <> _} = fields) do
|
||||
|
|
71
lib/livebook/hubs/broadcasts.ex
Normal file
71
lib/livebook/hubs/broadcasts.ex
Normal file
|
@ -0,0 +1,71 @@
|
|||
defmodule Livebook.Hubs.Broadcasts do
|
||||
@moduledoc false
|
||||
|
||||
alias Livebook.Secrets.Secret
|
||||
|
||||
@type broadcast :: :ok | {:error, term()}
|
||||
|
||||
@crud_topic "hubs:crud"
|
||||
@connection_topic "hubs:connection"
|
||||
@secrets_topic "hubs:secrets"
|
||||
|
||||
@doc """
|
||||
Broadcasts when hubs changed under `hubs:crud` topic
|
||||
"""
|
||||
@spec hubs_metadata_changed() :: broadcast()
|
||||
def hubs_metadata_changed do
|
||||
broadcast(@crud_topic, :hubs_metadata_changed)
|
||||
end
|
||||
|
||||
@doc """
|
||||
Broadcasts when hub connected under `hubs:connection` topic
|
||||
"""
|
||||
@spec hub_connected() :: broadcast()
|
||||
def hub_connected do
|
||||
broadcast(@connection_topic, :hub_connected)
|
||||
end
|
||||
|
||||
@doc """
|
||||
Broadcasts when hub disconnected under `hubs:connection` topic
|
||||
"""
|
||||
@spec hub_disconnected() :: broadcast()
|
||||
def hub_disconnected do
|
||||
broadcast(@connection_topic, :hub_disconnected)
|
||||
end
|
||||
|
||||
@doc """
|
||||
Broadcasts when hub had an error when connecting under `hubs:connection` topic
|
||||
"""
|
||||
@spec hub_connection_failed(String.t()) :: broadcast()
|
||||
def hub_connection_failed(reason) when is_binary(reason) do
|
||||
broadcast(@connection_topic, {:connection_error, reason})
|
||||
end
|
||||
|
||||
@doc """
|
||||
Broadcasts when hub had an error when disconnecting under `hubs:connection` topic
|
||||
"""
|
||||
@spec hub_disconnection_failed(String.t()) :: broadcast()
|
||||
def hub_disconnection_failed(reason) when is_binary(reason) do
|
||||
broadcast(@connection_topic, {:disconnection_error, reason})
|
||||
end
|
||||
|
||||
@doc """
|
||||
Broadcasts when hub received a new secret under `hubs:secrets` topic
|
||||
"""
|
||||
@spec secret_created(Secret.t()) :: broadcast()
|
||||
def secret_created(%Secret{} = secret) do
|
||||
broadcast(@secrets_topic, {:secret_created, secret})
|
||||
end
|
||||
|
||||
@doc """
|
||||
Broadcasts when hub received an updated secret under `hubs:secrets` topic
|
||||
"""
|
||||
@spec secret_updated(Secret.t()) :: broadcast()
|
||||
def secret_updated(%Secret{} = secret) do
|
||||
broadcast(@secrets_topic, {:secret_updated, secret})
|
||||
end
|
||||
|
||||
defp broadcast(topic, message) do
|
||||
Phoenix.PubSub.broadcast(Livebook.PubSub, topic, message)
|
||||
end
|
||||
end
|
|
@ -12,7 +12,7 @@ defmodule Livebook.Hubs.Enterprise do
|
|||
token: String.t() | nil,
|
||||
external_id: String.t() | nil,
|
||||
hub_name: String.t() | nil,
|
||||
hub_color: String.t() | nil
|
||||
hub_emoji: String.t() | nil
|
||||
}
|
||||
|
||||
embedded_schema do
|
||||
|
@ -20,7 +20,7 @@ defmodule Livebook.Hubs.Enterprise do
|
|||
field :token, :string
|
||||
field :external_id, :string
|
||||
field :hub_name, :string
|
||||
field :hub_color, Livebook.EctoTypes.HexColor
|
||||
field :hub_emoji, :string
|
||||
end
|
||||
|
||||
@fields ~w(
|
||||
|
@ -28,7 +28,7 @@ defmodule Livebook.Hubs.Enterprise do
|
|||
token
|
||||
external_id
|
||||
hub_name
|
||||
hub_color
|
||||
hub_emoji
|
||||
)a
|
||||
|
||||
@doc """
|
||||
|
@ -113,7 +113,7 @@ defimpl Livebook.Hubs.Provider, for: Livebook.Hubs.Enterprise do
|
|||
token: fields.token,
|
||||
external_id: fields.external_id,
|
||||
hub_name: fields.hub_name,
|
||||
hub_color: fields.hub_color
|
||||
hub_emoji: fields.hub_emoji
|
||||
}
|
||||
end
|
||||
|
||||
|
@ -122,7 +122,7 @@ defimpl Livebook.Hubs.Provider, for: Livebook.Hubs.Enterprise do
|
|||
id: enterprise.id,
|
||||
name: enterprise.hub_name,
|
||||
provider: enterprise,
|
||||
color: enterprise.hub_color
|
||||
emoji: enterprise.hub_emoji
|
||||
}
|
||||
end
|
||||
|
||||
|
@ -130,4 +130,8 @@ defimpl Livebook.Hubs.Provider, for: Livebook.Hubs.Enterprise do
|
|||
|
||||
def connect(%Livebook.Hubs.Enterprise{} = enterprise),
|
||||
do: {Livebook.Hubs.EnterpriseClient, enterprise}
|
||||
|
||||
def connected?(%Livebook.Hubs.Enterprise{id: id}) do
|
||||
Livebook.Hubs.EnterpriseClient.connected?(id)
|
||||
end
|
||||
end
|
||||
|
|
|
@ -2,21 +2,31 @@ defmodule Livebook.Hubs.EnterpriseClient do
|
|||
@moduledoc false
|
||||
use GenServer
|
||||
|
||||
alias Livebook.Hubs.Broadcasts
|
||||
alias Livebook.Hubs.Enterprise
|
||||
alias Livebook.Secrets.Secret
|
||||
alias Livebook.WebSocket.Server
|
||||
|
||||
@pubsub_topic "enterprise"
|
||||
@registry Livebook.HubsRegistry
|
||||
|
||||
defstruct [:server, :hub, secrets: []]
|
||||
defstruct [:server, :hub, connected?: false, secrets: []]
|
||||
|
||||
@doc """
|
||||
Connects the Enterprise client with WebSocket server.
|
||||
"""
|
||||
@spec start_link(Enterprise.t()) :: GenServer.on_start()
|
||||
def start_link(%Enterprise{} = enterprise) do
|
||||
GenServer.start_link(__MODULE__, enterprise, name: registry_name(enterprise))
|
||||
GenServer.start_link(__MODULE__, enterprise, name: registry_name(enterprise.id))
|
||||
end
|
||||
|
||||
@doc """
|
||||
Stops the WebSocket server.
|
||||
"""
|
||||
@spec stop(pid()) :: :ok
|
||||
def stop(pid) do
|
||||
pid |> GenServer.call(:get_server) |> GenServer.stop()
|
||||
|
||||
:ok
|
||||
end
|
||||
|
||||
@doc """
|
||||
|
@ -36,27 +46,15 @@ defmodule Livebook.Hubs.EnterpriseClient do
|
|||
end
|
||||
|
||||
@doc """
|
||||
Subscribe to WebSocket Server events.
|
||||
|
||||
## Messages
|
||||
|
||||
* `{:connect, :ok, :connected}`
|
||||
* `{:connect, :error, reason}`
|
||||
* `{:secret_created, %Secret{}}`
|
||||
* `{:secret_updated, %Secret{}}`
|
||||
|
||||
Returns if the given enterprise is connected.
|
||||
"""
|
||||
@spec subscribe() :: :ok | {:error, {:already_registered, pid()}}
|
||||
def subscribe do
|
||||
Phoenix.PubSub.subscribe(Livebook.PubSub, @pubsub_topic)
|
||||
end
|
||||
|
||||
@doc """
|
||||
Unsubscribes from `subscribe/0`.
|
||||
"""
|
||||
@spec unsubscribe() :: :ok
|
||||
def unsubscribe do
|
||||
Phoenix.PubSub.unsubscribe(Livebook.PubSub, @pubsub_topic)
|
||||
@spec connected?(String.t()) :: boolean()
|
||||
def connected?(id) do
|
||||
try do
|
||||
GenServer.call(registry_name(id), :connected?)
|
||||
catch
|
||||
:exit, _ -> false
|
||||
end
|
||||
end
|
||||
|
||||
## GenServer callbacks
|
||||
|
@ -78,44 +76,48 @@ defmodule Livebook.Hubs.EnterpriseClient do
|
|||
{:reply, state.secrets, state}
|
||||
end
|
||||
|
||||
@impl true
|
||||
def handle_info({:connect, _, _} = message, state) do
|
||||
broadcast_message(message)
|
||||
{:noreply, state}
|
||||
def handle_call(:connected?, _caller, state) do
|
||||
{:reply, state.connected?, state}
|
||||
end
|
||||
|
||||
def handle_info({:disconnect, :error, _} = message, state) do
|
||||
broadcast_message(message)
|
||||
{:noreply, state}
|
||||
@impl true
|
||||
def handle_info({:connect, :ok, _}, state) do
|
||||
Broadcasts.hub_connected()
|
||||
{:noreply, %{state | connected?: true}}
|
||||
end
|
||||
|
||||
def handle_info({:connect, :error, reason}, state) do
|
||||
Broadcasts.hub_connection_failed(reason)
|
||||
{:noreply, %{state | connected?: false}}
|
||||
end
|
||||
|
||||
def handle_info({:disconnect, :error, reason}, state) do
|
||||
Broadcasts.hub_disconnection_failed(reason)
|
||||
{:noreply, %{state | connected?: false}}
|
||||
end
|
||||
|
||||
def handle_info({:event, :secret_created, %{name: name, value: value}}, state) do
|
||||
secret = %Secret{name: name, value: value}
|
||||
broadcast_message({:secret_created, secret})
|
||||
Broadcasts.secret_created(secret)
|
||||
|
||||
{:noreply, put_secret(state, secret)}
|
||||
end
|
||||
|
||||
def handle_info({:event, :secret_updated, %{name: name, value: value}}, state) do
|
||||
secret = %Secret{name: name, value: value}
|
||||
broadcast_message({:secret_updated, secret})
|
||||
Broadcasts.secret_updated(secret)
|
||||
|
||||
{:noreply, put_secret(state, secret)}
|
||||
end
|
||||
|
||||
def handle_info({:disconnect, :ok, :disconnected}, state) do
|
||||
Broadcasts.hub_disconnected()
|
||||
{:stop, :normal, state}
|
||||
end
|
||||
|
||||
# Private
|
||||
|
||||
# Notifies interested processes about WebSocket Server messages.
|
||||
# Broadcasts the given message under the `"enterprise"` topic.
|
||||
defp broadcast_message(message) do
|
||||
Phoenix.PubSub.broadcast(Livebook.PubSub, @pubsub_topic, message)
|
||||
end
|
||||
|
||||
defp registry_name(%Enterprise{id: id}) do
|
||||
defp registry_name(id) do
|
||||
{:via, Registry, {@registry, id}}
|
||||
end
|
||||
|
||||
|
|
|
@ -10,7 +10,7 @@ defmodule Livebook.Hubs.Fly do
|
|||
id: String.t() | nil,
|
||||
access_token: String.t() | nil,
|
||||
hub_name: String.t() | nil,
|
||||
hub_color: String.t() | nil,
|
||||
hub_emoji: String.t() | nil,
|
||||
organization_id: String.t() | nil,
|
||||
organization_type: String.t() | nil,
|
||||
organization_name: String.t() | nil,
|
||||
|
@ -20,7 +20,7 @@ defmodule Livebook.Hubs.Fly do
|
|||
embedded_schema do
|
||||
field :access_token, :string
|
||||
field :hub_name, :string
|
||||
field :hub_color, Livebook.EctoTypes.HexColor
|
||||
field :hub_emoji, :string
|
||||
field :organization_id, :string
|
||||
field :organization_type, :string
|
||||
field :organization_name, :string
|
||||
|
@ -30,7 +30,7 @@ defmodule Livebook.Hubs.Fly do
|
|||
@fields ~w(
|
||||
access_token
|
||||
hub_name
|
||||
hub_color
|
||||
hub_emoji
|
||||
organization_id
|
||||
organization_name
|
||||
organization_type
|
||||
|
@ -116,7 +116,7 @@ defimpl Livebook.Hubs.Provider, for: Livebook.Hubs.Fly do
|
|||
| id: fields.id,
|
||||
access_token: fields.access_token,
|
||||
hub_name: fields.hub_name,
|
||||
hub_color: fields.hub_color,
|
||||
hub_emoji: fields.hub_emoji,
|
||||
organization_id: fields.organization_id,
|
||||
organization_type: fields.organization_type,
|
||||
organization_name: fields.organization_name,
|
||||
|
@ -129,11 +129,13 @@ defimpl Livebook.Hubs.Provider, for: Livebook.Hubs.Fly do
|
|||
id: fly.id,
|
||||
name: fly.hub_name,
|
||||
provider: fly,
|
||||
color: fly.hub_color
|
||||
emoji: fly.hub_emoji
|
||||
}
|
||||
end
|
||||
|
||||
def type(_fly), do: "fly"
|
||||
|
||||
def connect(_fly), do: nil
|
||||
|
||||
def connected?(_fly), do: false
|
||||
end
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
defmodule Livebook.Hubs.Local do
|
||||
@moduledoc false
|
||||
|
||||
defstruct [:id, :hub_name, :hub_color]
|
||||
defstruct [:id, :hub_name, :hub_emoji]
|
||||
end
|
||||
|
||||
defimpl Livebook.Hubs.Provider, for: Livebook.Hubs.Local do
|
||||
def load(%Livebook.Hubs.Local{} = local, fields) do
|
||||
%{local | id: fields.id, hub_name: fields.hub_name, hub_color: fields.hub_color}
|
||||
%{local | id: fields.id, hub_name: fields.hub_name, hub_emoji: fields.hub_emoji}
|
||||
end
|
||||
|
||||
def normalize(%Livebook.Hubs.Local{} = local) do
|
||||
|
@ -14,11 +14,13 @@ defimpl Livebook.Hubs.Provider, for: Livebook.Hubs.Local do
|
|||
id: local.id,
|
||||
name: local.hub_name,
|
||||
provider: local,
|
||||
color: local.hub_color
|
||||
emoji: local.hub_emoji
|
||||
}
|
||||
end
|
||||
|
||||
def type(_local), do: "local"
|
||||
|
||||
def connect(_local), do: nil
|
||||
|
||||
def connected?(_local), do: false
|
||||
end
|
||||
|
|
|
@ -1,12 +1,13 @@
|
|||
defmodule Livebook.Hubs.Metadata do
|
||||
@moduledoc false
|
||||
|
||||
defstruct [:id, :name, :provider, :color]
|
||||
defstruct [:id, :name, :provider, :emoji, connected?: false]
|
||||
|
||||
@type t :: %__MODULE__{
|
||||
id: String.t(),
|
||||
name: String.t(),
|
||||
provider: struct(),
|
||||
color: String.t()
|
||||
emoji: String.t(),
|
||||
connected?: boolean()
|
||||
}
|
||||
end
|
||||
|
|
|
@ -24,4 +24,10 @@ defprotocol Livebook.Hubs.Provider do
|
|||
"""
|
||||
@spec connect(struct()) :: Supervisor.child_spec() | module() | {module(), any()} | nil
|
||||
def connect(struct)
|
||||
|
||||
@doc """
|
||||
Gets the connection status of the given struct.
|
||||
"""
|
||||
@spec connected?(struct()) :: boolean()
|
||||
def connected?(struct)
|
||||
end
|
||||
|
|
|
@ -2,7 +2,6 @@ defmodule LivebookWeb.FormHelpers do
|
|||
@moduledoc """
|
||||
Conveniences for translating and building error messages.
|
||||
"""
|
||||
|
||||
use Phoenix.Component
|
||||
|
||||
import Phoenix.HTML.Form
|
||||
|
@ -55,6 +54,38 @@ defmodule LivebookWeb.FormHelpers do
|
|||
"""
|
||||
end
|
||||
|
||||
@doc """
|
||||
Emoji input.
|
||||
"""
|
||||
def emoji_input(assigns) do
|
||||
~H"""
|
||||
<div id={@id} class="flex border-[1px] bg-gray-50 rounded-lg space-x-4 items-center">
|
||||
<div id={"#{@id}-picker"} class="grid grid-cols-1 md:grid-cols-3 w-full" phx-hook="EmojiPicker">
|
||||
<div class="place-content-start">
|
||||
<div class="p-1 pl-3">
|
||||
<span id={"#{@id}-preview"} data-emoji-preview><%= input_value(@form, @field) %></span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div />
|
||||
|
||||
<div class="flex items-center place-content-end">
|
||||
<button
|
||||
id={"#{@id}-button"}
|
||||
type="button"
|
||||
data-emoji-button
|
||||
class="p-1 pl-3 pr-3 rounded-tr-lg rounded-br-lg bg-gray-50 hover:bg-gray-100 active:bg-gray-200 border-l-[1px] bg-white flex justify-center items-center cursor-pointer"
|
||||
>
|
||||
<.remix_icon icon="emotion-line" class="text-xl" />
|
||||
</button>
|
||||
</div>
|
||||
<div id={"#{@id}-container"} data-emoji-container class="absolute mt-10 hidden" />
|
||||
<%= hidden_input(@form, @field, class: "hidden emoji-picker-input", "data-emoji-input": true) %>
|
||||
</div>
|
||||
</div>
|
||||
"""
|
||||
end
|
||||
|
||||
@doc """
|
||||
Translates an error message.
|
||||
"""
|
||||
|
|
|
@ -6,7 +6,7 @@ defmodule LivebookWeb.SidebarHook do
|
|||
|
||||
def on_mount(:default, _params, _session, socket) do
|
||||
if connected?(socket) do
|
||||
Livebook.Hubs.subscribe()
|
||||
Livebook.Hubs.subscribe([:crud, :connection])
|
||||
end
|
||||
|
||||
socket =
|
||||
|
@ -18,8 +18,16 @@ defmodule LivebookWeb.SidebarHook do
|
|||
{:cont, socket}
|
||||
end
|
||||
|
||||
defp handle_info({:hubs_metadata_changed, hubs}, socket) do
|
||||
{:halt, assign(socket, saved_hubs: hubs)}
|
||||
@connection_events ~w(hub_connected hub_disconnected hubs_metadata_changed)a
|
||||
|
||||
defp handle_info(event, socket) when event in @connection_events do
|
||||
{:halt, assign(socket, saved_hubs: Livebook.Hubs.get_metadatas())}
|
||||
end
|
||||
|
||||
@error_events ~w(connection_error disconnection_error)a
|
||||
|
||||
defp handle_info({event, _reason}, socket) when event in @error_events do
|
||||
{:halt, assign(socket, saved_hubs: Livebook.Hubs.get_metadatas())}
|
||||
end
|
||||
|
||||
defp handle_info(_event, socket), do: {:cont, socket}
|
||||
|
|
|
@ -21,7 +21,7 @@ defmodule LivebookWeb.UserHook do
|
|||
{:user_change, %{id: id} = user},
|
||||
%{assigns: %{current_user: %{id: id}}} = socket
|
||||
) do
|
||||
{:cont, assign(socket, :current_user, user)}
|
||||
{:halt, assign(socket, :current_user, user)}
|
||||
end
|
||||
|
||||
defp info(_message, socket), do: {:cont, socket}
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
defmodule LivebookWeb.Hub.Edit.EnterpriseComponent do
|
||||
use LivebookWeb, :live_component
|
||||
|
||||
alias Livebook.EctoTypes.HexColor
|
||||
alias Livebook.Hubs.Enterprise
|
||||
|
||||
@impl true
|
||||
|
@ -35,13 +34,9 @@ defmodule LivebookWeb.Hub.Edit.EnterpriseComponent do
|
|||
phx-debounce="blur"
|
||||
>
|
||||
<div class="grid grid-cols-1 md:grid-cols-1 gap-3">
|
||||
<.input_wrapper form={f} field={:hub_color} class="flex flex-col space-y-1">
|
||||
<div class="input-label">Color</div>
|
||||
<.hex_color_input
|
||||
form={f}
|
||||
field={:hub_color}
|
||||
randomize={JS.push("randomize_color", target: @myself)}
|
||||
/>
|
||||
<.input_wrapper form={f} field={:hub_emoji} class="flex flex-col space-y-1">
|
||||
<div class="input-label">Emoji</div>
|
||||
<.emoji_input id="enterprise-emoji-input" form={f} field={:hub_emoji} />
|
||||
</.input_wrapper>
|
||||
</div>
|
||||
|
||||
|
@ -58,10 +53,6 @@ defmodule LivebookWeb.Hub.Edit.EnterpriseComponent do
|
|||
end
|
||||
|
||||
@impl true
|
||||
def handle_event("randomize_color", _, socket) do
|
||||
handle_event("validate", %{"enterprise" => %{"hub_color" => HexColor.random()}}, socket)
|
||||
end
|
||||
|
||||
def handle_event("save", %{"enterprise" => params}, socket) do
|
||||
case Enterprise.update_hub(socket.assigns.hub, params) do
|
||||
{:ok, hub} ->
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
defmodule LivebookWeb.Hub.Edit.FlyComponent do
|
||||
use LivebookWeb, :live_component
|
||||
|
||||
alias Livebook.EctoTypes.HexColor
|
||||
alias Livebook.Hubs.{Fly, FlyClient}
|
||||
|
||||
@impl true
|
||||
|
@ -76,13 +75,9 @@ defmodule LivebookWeb.Hub.Edit.FlyComponent do
|
|||
<%= text_input(f, :hub_name, class: "input") %>
|
||||
</.input_wrapper>
|
||||
|
||||
<.input_wrapper form={f} field={:hub_color} class="flex flex-col space-y-1">
|
||||
<div class="input-label">Color</div>
|
||||
<.hex_color_input
|
||||
form={f}
|
||||
field={:hub_color}
|
||||
randomize={JS.push("randomize_color", target: @myself)}
|
||||
/>
|
||||
<.input_wrapper form={f} field={:hub_emoji} class="flex flex-col space-y-1">
|
||||
<div class="input-label">Emoji</div>
|
||||
<.emoji_input id="fly-emoji-input" form={f} field={:hub_emoji} />
|
||||
</.input_wrapper>
|
||||
</div>
|
||||
|
||||
|
@ -134,10 +129,6 @@ defmodule LivebookWeb.Hub.Edit.FlyComponent do
|
|||
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} ->
|
||||
|
|
|
@ -3,13 +3,12 @@ defmodule LivebookWeb.Hub.New.EnterpriseComponent do
|
|||
|
||||
import Ecto.Changeset, only: [get_field: 2]
|
||||
|
||||
alias Livebook.EctoTypes.HexColor
|
||||
alias Livebook.Hubs.{Enterprise, EnterpriseClient}
|
||||
|
||||
@impl true
|
||||
def update(assigns, socket) do
|
||||
if connected?(socket) do
|
||||
EnterpriseClient.subscribe()
|
||||
Livebook.Hubs.subscribe(:connection)
|
||||
end
|
||||
|
||||
{:ok,
|
||||
|
@ -82,13 +81,9 @@ defmodule LivebookWeb.Hub.New.EnterpriseComponent do
|
|||
<%= text_input(f, :hub_name, class: "input", readonly: true) %>
|
||||
</.input_wrapper>
|
||||
|
||||
<.input_wrapper form={f} field={:hub_color} class="flex flex-col space-y-1">
|
||||
<div class="input-label">Color</div>
|
||||
<.hex_color_input
|
||||
form={f}
|
||||
field={:hub_color}
|
||||
randomize={JS.push("randomize_color", target: @myself)}
|
||||
/>
|
||||
<.input_wrapper form={f} field={:hub_emoji} class="flex flex-col space-y-1">
|
||||
<div class="input-label">Emoji</div>
|
||||
<.emoji_input id="enterprise-emoji-input" form={f} field={:hub_emoji} />
|
||||
</.input_wrapper>
|
||||
</div>
|
||||
|
||||
|
@ -109,20 +104,26 @@ defmodule LivebookWeb.Hub.New.EnterpriseComponent do
|
|||
token = get_field(socket.assigns.changeset, :token)
|
||||
|
||||
base = %Enterprise{
|
||||
id: "enterprise-placeholder",
|
||||
token: token,
|
||||
external_id: "placeholder",
|
||||
url: url,
|
||||
hub_name: "Enterprise",
|
||||
hub_color: HexColor.random()
|
||||
hub_emoji: "🏭"
|
||||
}
|
||||
|
||||
{:ok, pid} = EnterpriseClient.start_link(base)
|
||||
|
||||
receive do
|
||||
{:connect, :error, reason} ->
|
||||
GenServer.stop(pid)
|
||||
handle_error(reason, socket)
|
||||
{:connection_error, reason} ->
|
||||
EnterpriseClient.stop(pid)
|
||||
|
||||
{:connect, :ok, :connected} ->
|
||||
{:noreply,
|
||||
socket
|
||||
|> put_flash(:error, "Failed to connect with Enterprise: " <> reason)
|
||||
|> push_patch(to: Routes.hub_path(socket, :new))}
|
||||
|
||||
:hub_connected ->
|
||||
session_request =
|
||||
LivebookProto.SessionRequest.new!(app_version: Livebook.Config.app_version())
|
||||
|
||||
|
@ -134,16 +135,16 @@ defmodule LivebookWeb.Hub.New.EnterpriseComponent do
|
|||
{:noreply, assign(socket, pid: pid, changeset: changeset, base: base)}
|
||||
|
||||
{:error, reason} ->
|
||||
GenServer.stop(pid)
|
||||
handle_error(reason, socket)
|
||||
EnterpriseClient.stop(pid)
|
||||
|
||||
{:noreply,
|
||||
socket
|
||||
|> put_flash(:error, "Failed to connect with Enterprise: " <> reason)
|
||||
|> push_patch(to: Routes.hub_path(socket, :new))}
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def handle_event("randomize_color", _, socket) do
|
||||
handle_event("validate", %{"enterprise" => %{"hub_color" => HexColor.random()}}, socket)
|
||||
end
|
||||
|
||||
def handle_event("save", %{"enterprise" => params}, socket) do
|
||||
if socket.assigns.changeset.valid? do
|
||||
case Enterprise.create_hub(socket.assigns.base, params) do
|
||||
|
@ -168,23 +169,4 @@ defmodule LivebookWeb.Hub.New.EnterpriseComponent do
|
|||
def handle_event("validate", %{"enterprise" => attrs}, socket) do
|
||||
{:noreply, assign(socket, changeset: Enterprise.change_hub(socket.assigns.base, attrs))}
|
||||
end
|
||||
|
||||
def handle_error(%{reason: :econnrefused}, socket) do
|
||||
show_connect_error("Failed to connect with given URL", socket)
|
||||
end
|
||||
|
||||
def handle_error(%{reason: _}, socket) do
|
||||
show_connect_error("Failed to connect with Enterprise", socket)
|
||||
end
|
||||
|
||||
def handle_error(reason, socket) do
|
||||
show_connect_error(reason, socket)
|
||||
end
|
||||
|
||||
defp show_connect_error(message, socket) do
|
||||
{:noreply,
|
||||
socket
|
||||
|> put_flash(:error, message)
|
||||
|> push_patch(to: Routes.hub_path(socket, :new))}
|
||||
end
|
||||
end
|
||||
|
|
|
@ -3,7 +3,6 @@ defmodule LivebookWeb.Hub.New.FlyComponent do
|
|||
|
||||
import Ecto.Changeset, only: [get_field: 2, add_error: 3]
|
||||
|
||||
alias Livebook.EctoTypes.HexColor
|
||||
alias Livebook.Hubs.{Fly, FlyClient}
|
||||
|
||||
@impl true
|
||||
|
@ -60,13 +59,9 @@ defmodule LivebookWeb.Hub.New.FlyComponent do
|
|||
<%= text_input(f, :hub_name, class: "input") %>
|
||||
</.input_wrapper>
|
||||
|
||||
<.input_wrapper form={f} field={:hub_color} class="flex flex-col space-y-1">
|
||||
<div class="input-label">Color</div>
|
||||
<.hex_color_input
|
||||
form={f}
|
||||
field={:hub_color}
|
||||
randomize={JS.push("randomize_color", target: @myself)}
|
||||
/>
|
||||
<.input_wrapper form={f} field={:hub_emoji} class="flex flex-col space-y-1">
|
||||
<div class="input-label">Emoji</div>
|
||||
<.emoji_input id="fly-emoji-input" form={f} field={:hub_emoji} />
|
||||
</.input_wrapper>
|
||||
</div>
|
||||
|
||||
|
@ -86,7 +81,7 @@ defmodule LivebookWeb.Hub.New.FlyComponent do
|
|||
case FlyClient.fetch_apps(token) do
|
||||
{:ok, apps} ->
|
||||
opts = select_options(apps)
|
||||
base = %Fly{access_token: token, hub_color: HexColor.random()}
|
||||
base = %Fly{access_token: token, hub_emoji: "🚀"}
|
||||
changeset = Fly.change_hub(base)
|
||||
|
||||
{:noreply,
|
||||
|
@ -103,10 +98,6 @@ defmodule LivebookWeb.Hub.New.FlyComponent do
|
|||
end
|
||||
end
|
||||
|
||||
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
|
||||
if socket.assigns.changeset.valid? do
|
||||
case Fly.create_hub(socket.assigns.selected_app, params) do
|
||||
|
|
|
@ -5,6 +5,7 @@ defmodule LivebookWeb.LayoutHelpers do
|
|||
import LivebookWeb.UserHelpers
|
||||
|
||||
alias Phoenix.LiveView.JS
|
||||
alias Livebook.Hubs.Provider
|
||||
alias LivebookWeb.Router.Helpers, as: Routes
|
||||
|
||||
@doc """
|
||||
|
@ -165,15 +166,9 @@ defmodule LivebookWeb.LayoutHelpers do
|
|||
end
|
||||
|
||||
defp sidebar_link(assigns) do
|
||||
assigns = assign_new(assigns, :icon_style, fn -> nil end)
|
||||
|
||||
~H"""
|
||||
<%= live_redirect to: @to, class: "h-7 flex items-center hover:text-white #{sidebar_link_text_color(@to, @current)} border-l-4 #{sidebar_link_border_color(@to, @current)} hover:border-white" do %>
|
||||
<.remix_icon
|
||||
icon={@icon}
|
||||
class="text-lg leading-6 w-[56px] flex justify-center"
|
||||
style={@icon_style}
|
||||
/>
|
||||
<.remix_icon icon={@icon} class="text-lg leading-6 w-[56px] flex justify-center" />
|
||||
<span class="text-sm font-medium">
|
||||
<%= @title %>
|
||||
</span>
|
||||
|
@ -181,6 +176,28 @@ defmodule LivebookWeb.LayoutHelpers do
|
|||
"""
|
||||
end
|
||||
|
||||
defp sidebar_hub_link(assigns) do
|
||||
~H"""
|
||||
<%= live_redirect to: @to, class: "h-7 flex items-center hover:text-white #{sidebar_link_text_color(@to, @current)} border-l-4 #{sidebar_link_border_color(@to, @current)} hover:border-white" do %>
|
||||
<div class="text-lg leading-6 w-[56px] flex justify-center">
|
||||
<span class="relative">
|
||||
<%= @hub.emoji %>
|
||||
|
||||
<%= if Provider.connect(@hub.provider) do %>
|
||||
<div class={[
|
||||
"absolute w-[10px] h-[10px] border-gray-900 border-2 rounded-full right-0 bottom-0",
|
||||
if(@hub.connected?, do: "bg-green-400", else: "bg-red-400")
|
||||
]} />
|
||||
<% end %>
|
||||
</span>
|
||||
</div>
|
||||
<span class="text-sm font-medium">
|
||||
<%= @hub.name %>
|
||||
</span>
|
||||
<% end %>
|
||||
"""
|
||||
end
|
||||
|
||||
defp hub_section(assigns) do
|
||||
~H"""
|
||||
<%= if Livebook.Config.feature_flag_enabled?(:hub) do %>
|
||||
|
@ -191,10 +208,8 @@ defmodule LivebookWeb.LayoutHelpers do
|
|||
</div>
|
||||
|
||||
<%= for hub <- @hubs do %>
|
||||
<.sidebar_link
|
||||
title={hub.name}
|
||||
icon="checkbox-blank-circle-fill"
|
||||
icon_style={"color: #{hub.color}"}
|
||||
<.sidebar_hub_link
|
||||
hub={hub}
|
||||
to={Routes.hub_path(@socket, :edit, hub.id)}
|
||||
current={@current_page}
|
||||
/>
|
||||
|
|
|
@ -26,8 +26,7 @@ defmodule LivebookWeb.SessionLive do
|
|||
|
||||
Session.subscribe(session_id)
|
||||
Secrets.subscribe()
|
||||
# TODO: Move this to Hubs.subscribe([:secrets]) and rename all "enterprise" to "hubs"
|
||||
EnterpriseClient.subscribe()
|
||||
Hubs.subscribe(:secrets)
|
||||
|
||||
{data, client_id}
|
||||
else
|
||||
|
@ -64,7 +63,7 @@ defmodule LivebookWeb.SessionLive do
|
|||
autofocus_cell_id: autofocus_cell_id(data.notebook),
|
||||
page_title: get_page_title(data.notebook.name),
|
||||
livebook_secrets: Secrets.fetch_secrets() |> Map.new(&{&1.name, &1.value}),
|
||||
enterprise_secrets: fetch_enterprise_secrets(),
|
||||
hub_secrets: get_hub_secrets(),
|
||||
select_secret_ref: nil,
|
||||
select_secret_options: nil
|
||||
)
|
||||
|
@ -186,7 +185,7 @@ defmodule LivebookWeb.SessionLive do
|
|||
<.secrets_list
|
||||
data_view={@data_view}
|
||||
livebook_secrets={@livebook_secrets}
|
||||
enterprise_secrets={@enterprise_secrets}
|
||||
hub_secrets={@hub_secrets}
|
||||
session={@session}
|
||||
socket={@socket}
|
||||
/>
|
||||
|
@ -739,13 +738,13 @@ defmodule LivebookWeb.SessionLive do
|
|||
<%= if Livebook.Config.feature_flag_enabled?(:hub) do %>
|
||||
<div class="mt-16">
|
||||
<h3 class="uppercase text-sm font-semibold text-gray-500">
|
||||
Enterprise secrets
|
||||
Hub secrets
|
||||
</h3>
|
||||
<span class="text-sm text-gray-500">Available in all sessions</span>
|
||||
</div>
|
||||
|
||||
<div class="flex flex-col space-y-4 mt-6">
|
||||
<%= for {secret_name, secret_value} <- Enum.sort(@enterprise_secrets) do %>
|
||||
<%= for {secret_name, secret_value} <- Enum.sort(@hub_secrets) do %>
|
||||
<div
|
||||
class="flex flex-col text-gray-500 rounded-lg px-2 pt-1"
|
||||
id={"enterprise-secret-#{secret_name}-wrapper"}
|
||||
|
@ -1450,14 +1449,14 @@ defmodule LivebookWeb.SessionLive do
|
|||
def handle_info({:secret_created, %Secrets.Secret{}}, socket) do
|
||||
{:noreply,
|
||||
socket
|
||||
|> assign(enterprise_secrets: fetch_enterprise_secrets())
|
||||
|> assign(hub_secrets: get_hub_secrets())
|
||||
|> put_flash(:info, "A new secret has been created on your Livebook Enterprise")}
|
||||
end
|
||||
|
||||
def handle_info({:secret_updated, %Secrets.Secret{}}, socket) do
|
||||
{:noreply,
|
||||
socket
|
||||
|> assign(enterprise_secrets: fetch_enterprise_secrets())
|
||||
|> assign(hub_secrets: get_hub_secrets())
|
||||
|> put_flash(:info, "An existing secret has been updated on your Livebook Enterprise")}
|
||||
end
|
||||
|
||||
|
@ -2298,7 +2297,7 @@ defmodule LivebookWeb.SessionLive do
|
|||
secret in secrets
|
||||
end
|
||||
|
||||
defp fetch_enterprise_secrets do
|
||||
defp get_hub_secrets do
|
||||
for connected_hub <- Hubs.get_connected_hubs(),
|
||||
secret <- EnterpriseClient.list_cached_secrets(connected_hub.pid),
|
||||
into: %{},
|
||||
|
|
|
@ -6,7 +6,7 @@ defmodule Livebook.Hubs.EnterpriseClientTest do
|
|||
alias Livebook.Secrets.Secret
|
||||
|
||||
setup do
|
||||
EnterpriseClient.subscribe()
|
||||
Livebook.Hubs.subscribe([:connection, :secrets])
|
||||
:ok
|
||||
end
|
||||
|
||||
|
@ -15,21 +15,21 @@ defmodule Livebook.Hubs.EnterpriseClientTest do
|
|||
enterprise = build(:enterprise, url: url, token: token)
|
||||
|
||||
EnterpriseClient.start_link(enterprise)
|
||||
assert_receive {:connect, :ok, :connected}
|
||||
assert_receive :hub_connected
|
||||
end
|
||||
|
||||
test "rejects the websocket with invalid address", %{token: token} do
|
||||
enterprise = build(:enterprise, url: "http://localhost:9999", token: token)
|
||||
|
||||
EnterpriseClient.start_link(enterprise)
|
||||
assert_receive {:connect, :error, "connection refused"}
|
||||
assert_receive {:connection_error, "connection refused"}
|
||||
end
|
||||
|
||||
test "rejects the web socket connection with invalid credentials", %{url: url} do
|
||||
enterprise = build(:enterprise, url: url, token: "foo")
|
||||
|
||||
EnterpriseClient.start_link(enterprise)
|
||||
assert_receive {:connect, :error, reason}
|
||||
assert_receive {:connection_error, reason}
|
||||
assert reason =~ "the given token is invalid"
|
||||
end
|
||||
end
|
||||
|
@ -38,8 +38,7 @@ defmodule Livebook.Hubs.EnterpriseClientTest do
|
|||
setup %{url: url, token: token} do
|
||||
enterprise = build(:enterprise, url: url, token: token)
|
||||
EnterpriseClient.start_link(enterprise)
|
||||
|
||||
assert_receive {:connect, :ok, :connected}
|
||||
assert_receive :hub_connected
|
||||
|
||||
:ok
|
||||
end
|
||||
|
|
|
@ -10,7 +10,7 @@ defmodule Livebook.Hubs.ProviderTest do
|
|||
assert Provider.normalize(fly) == %Metadata{
|
||||
id: fly.id,
|
||||
name: fly.hub_name,
|
||||
color: fly.hub_color,
|
||||
emoji: fly.hub_emoji,
|
||||
provider: fly
|
||||
}
|
||||
end
|
||||
|
|
|
@ -23,7 +23,7 @@ defmodule Livebook.HubsTest do
|
|||
assert Hubs.get_metadatas() == [
|
||||
%Hubs.Metadata{
|
||||
id: "fly-livebook",
|
||||
color: fly.hub_color,
|
||||
emoji: fly.hub_emoji,
|
||||
name: fly.hub_name,
|
||||
provider: fly
|
||||
}
|
||||
|
@ -60,9 +60,9 @@ defmodule Livebook.HubsTest do
|
|||
|
||||
test "save_hub/1 updates hub" do
|
||||
fly = insert_hub(:fly, id: "fly-foo2")
|
||||
Hubs.save_hub(%{fly | hub_color: "#FFFFFF"})
|
||||
Hubs.save_hub(%{fly | hub_emoji: "🐈"})
|
||||
|
||||
refute Hubs.fetch_hub!("fly-foo2") == fly
|
||||
assert Hubs.fetch_hub!("fly-foo2").hub_color == "#FFFFFF"
|
||||
assert Hubs.fetch_hub!("fly-foo2").hub_emoji == "🐈"
|
||||
end
|
||||
end
|
||||
|
|
|
@ -40,7 +40,7 @@ defmodule LivebookWeb.Hub.EditLiveTest do
|
|||
|
||||
attrs = %{
|
||||
"hub_name" => "Personal Hub",
|
||||
"hub_color" => "#FF00FF"
|
||||
"hub_emoji" => "🐈"
|
||||
}
|
||||
|
||||
view
|
||||
|
@ -59,7 +59,7 @@ defmodule LivebookWeb.Hub.EditLiveTest do
|
|||
|
||||
assert render(view) =~ "Hub updated successfully"
|
||||
|
||||
assert_hub(view, conn, %{hub | hub_color: attrs["hub_color"], hub_name: attrs["hub_name"]})
|
||||
assert_hub(view, conn, %{hub | hub_emoji: attrs["hub_emoji"], hub_name: attrs["hub_name"]})
|
||||
refute Hubs.fetch_hub!(hub.id) == hub
|
||||
end
|
||||
|
||||
|
@ -195,7 +195,7 @@ defmodule LivebookWeb.Hub.EditLiveTest do
|
|||
hub = insert_hub(:enterprise)
|
||||
{:ok, view, _html} = live(conn, Routes.hub_path(conn, :edit, hub.id))
|
||||
|
||||
attrs = %{"hub_color" => "#FF00FF"}
|
||||
attrs = %{"hub_emoji" => "🐈"}
|
||||
|
||||
view
|
||||
|> element("#enterprise-form")
|
||||
|
@ -213,7 +213,7 @@ defmodule LivebookWeb.Hub.EditLiveTest do
|
|||
|
||||
assert render(view) =~ "Hub updated successfully"
|
||||
|
||||
assert_hub(view, conn, %{hub | hub_color: attrs["hub_color"]})
|
||||
assert_hub(view, conn, %{hub | hub_emoji: attrs["hub_emoji"]})
|
||||
refute Hubs.fetch_hub!(hub.id) == hub
|
||||
end
|
||||
end
|
||||
|
@ -221,7 +221,7 @@ defmodule LivebookWeb.Hub.EditLiveTest do
|
|||
defp assert_hub(view, conn, hub) do
|
||||
hubs_html = view |> element("#hubs") |> render()
|
||||
|
||||
assert hubs_html =~ ~s/style="color: #{hub.hub_color}"/
|
||||
assert hubs_html =~ hub.hub_emoji
|
||||
assert hubs_html =~ Routes.hub_path(conn, :edit, hub.id)
|
||||
assert hubs_html =~ hub.hub_name
|
||||
end
|
||||
|
|
|
@ -37,7 +37,7 @@ defmodule LivebookWeb.Hub.New.EnterpriseComponentTest do
|
|||
"url" => url,
|
||||
"token" => token,
|
||||
"hub_name" => "Enterprise",
|
||||
"hub_color" => "#FF00FF"
|
||||
"hub_emoji" => "🐈"
|
||||
}
|
||||
|
||||
view
|
||||
|
@ -58,7 +58,7 @@ defmodule LivebookWeb.Hub.New.EnterpriseComponentTest do
|
|||
assert render(view) =~ "Hub added successfully"
|
||||
|
||||
hubs_html = view |> element("#hubs") |> render()
|
||||
assert hubs_html =~ ~s/style="color: #FF00FF"/
|
||||
assert hubs_html =~ "🐈"
|
||||
assert hubs_html =~ "/hub/enterprise-#{id}"
|
||||
assert hubs_html =~ "Enterprise"
|
||||
end
|
||||
|
@ -94,8 +94,13 @@ defmodule LivebookWeb.Hub.New.EnterpriseComponentTest do
|
|||
stop_new_instance(name)
|
||||
end
|
||||
|
||||
test "fails to create existing hub", %{conn: conn, url: url, token: token} do
|
||||
node = EnterpriseServer.get_node()
|
||||
test "fails to create existing hub", %{test: name, conn: conn} do
|
||||
start_new_instance(name)
|
||||
|
||||
node = EnterpriseServer.get_node(name)
|
||||
url = EnterpriseServer.url(name)
|
||||
token = EnterpriseServer.token(name)
|
||||
|
||||
id = :erpc.call(node, Enterprise.Integration, :fetch_env!, [])
|
||||
user = :erpc.call(node, Enterprise.Integration, :create_user, [])
|
||||
|
||||
|
@ -135,7 +140,7 @@ defmodule LivebookWeb.Hub.New.EnterpriseComponentTest do
|
|||
"url" => url,
|
||||
"token" => token,
|
||||
"hub_name" => "Enterprise",
|
||||
"hub_color" => "#FFFFFF"
|
||||
"hub_emoji" => "🐈"
|
||||
}
|
||||
|
||||
view
|
||||
|
@ -151,11 +156,13 @@ defmodule LivebookWeb.Hub.New.EnterpriseComponentTest do
|
|||
|> render_submit(%{"enterprise" => attrs}) =~ "already exists"
|
||||
|
||||
hubs_html = view |> element("#hubs") |> render()
|
||||
assert hubs_html =~ ~s/style="color: #{hub.hub_color}"/
|
||||
assert hubs_html =~ hub.hub_emoji
|
||||
assert hubs_html =~ Routes.hub_path(conn, :edit, hub.id)
|
||||
assert hubs_html =~ hub.hub_name
|
||||
|
||||
assert Hubs.fetch_hub!(hub.id) == hub
|
||||
after
|
||||
stop_new_instance(name)
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -36,7 +36,7 @@ defmodule LivebookWeb.Hub.NewLiveTest do
|
|||
"access_token" => "dummy access token",
|
||||
"application_id" => "123456789",
|
||||
"hub_name" => "My Foo Hub",
|
||||
"hub_color" => "#FF00FF"
|
||||
"hub_emoji" => "🐈"
|
||||
}
|
||||
|
||||
view
|
||||
|
@ -55,17 +55,11 @@ defmodule LivebookWeb.Hub.NewLiveTest do
|
|||
|
||||
assert render(view) =~ "Hub added successfully"
|
||||
|
||||
assert view
|
||||
|> element("#hubs")
|
||||
|> render() =~ ~s/style="color: #FF00FF"/
|
||||
hubs_html = view |> element("#hubs") |> render()
|
||||
|
||||
assert view
|
||||
|> element("#hubs")
|
||||
|> render() =~ "/hub/fly-123456789"
|
||||
|
||||
assert view
|
||||
|> element("#hubs")
|
||||
|> render() =~ "My Foo Hub"
|
||||
assert hubs_html =~ "🐈"
|
||||
assert hubs_html =~ "/hub/fly-123456789"
|
||||
assert hubs_html =~ "My Foo Hub"
|
||||
end
|
||||
|
||||
test "fails to create existing hub", %{conn: conn} do
|
||||
|
@ -87,7 +81,7 @@ defmodule LivebookWeb.Hub.NewLiveTest do
|
|||
"access_token" => "dummy access token",
|
||||
"application_id" => "foo",
|
||||
"hub_name" => "My Foo Hub",
|
||||
"hub_color" => "#FF00FF"
|
||||
"hub_emoji" => "🐈"
|
||||
}
|
||||
|
||||
view
|
||||
|
@ -102,18 +96,7 @@ defmodule LivebookWeb.Hub.NewLiveTest do
|
|||
|> element("#fly-form")
|
||||
|> render_submit(%{"fly" => attrs}) =~ "already exists"
|
||||
|
||||
assert view
|
||||
|> element("#hubs")
|
||||
|> render() =~ ~s/style="color: #{hub.hub_color}"/
|
||||
|
||||
assert view
|
||||
|> element("#hubs")
|
||||
|> render() =~ Routes.hub_path(conn, :edit, hub.id)
|
||||
|
||||
assert view
|
||||
|> element("#hubs")
|
||||
|> render() =~ hub.hub_name
|
||||
|
||||
assert_hub(view, conn, hub)
|
||||
assert Hubs.fetch_hub!(hub.id) == hub
|
||||
end
|
||||
end
|
||||
|
@ -177,4 +160,12 @@ defmodule LivebookWeb.Hub.NewLiveTest do
|
|||
|
||||
%{"data" => %{"app" => app}}
|
||||
end
|
||||
|
||||
defp assert_hub(view, conn, hub) do
|
||||
hubs_html = view |> element("#hubs") |> render()
|
||||
|
||||
assert hubs_html =~ hub.hub_emoji
|
||||
assert hubs_html =~ Routes.hub_path(conn, :edit, hub.id)
|
||||
assert hubs_html =~ hub.hub_name
|
||||
end
|
||||
end
|
||||
|
|
|
@ -9,7 +9,8 @@ defmodule LivebookWeb.SessionLive.SecretsComponentTest do
|
|||
|
||||
describe "enterprise" do
|
||||
setup %{url: url, token: token} do
|
||||
id = Livebook.Utils.random_id()
|
||||
node = EnterpriseServer.get_node()
|
||||
id = :erpc.call(node, Enterprise.Integration, :fetch_env!, [])
|
||||
Livebook.Hubs.delete_hub("enterprise-#{id}")
|
||||
|
||||
enterprise =
|
||||
|
@ -21,8 +22,7 @@ defmodule LivebookWeb.SessionLive.SecretsComponentTest do
|
|||
)
|
||||
|
||||
{:ok, session} = Sessions.create_session(notebook: Livebook.Notebook.new())
|
||||
Livebook.Hubs.EnterpriseClient.subscribe()
|
||||
Livebook.Hubs.connect_hubs()
|
||||
Livebook.Hubs.subscribe(:secrets)
|
||||
|
||||
on_exit(fn ->
|
||||
Session.close(session.pid)
|
||||
|
|
|
@ -17,7 +17,7 @@ defmodule Livebook.Factory do
|
|||
%Livebook.Hubs.Fly{
|
||||
id: "fly-foo-bar-baz",
|
||||
hub_name: "My Personal Hub",
|
||||
hub_color: "#FF00FF",
|
||||
hub_emoji: "🚀",
|
||||
access_token: Livebook.Utils.random_cookie(),
|
||||
organization_id: Livebook.Utils.random_id(),
|
||||
organization_type: "PERSONAL",
|
||||
|
@ -36,7 +36,7 @@ defmodule Livebook.Factory do
|
|||
%Livebook.Hubs.Enterprise{
|
||||
id: "enterprise-#{id}",
|
||||
hub_name: "Enterprise",
|
||||
hub_color: "#FF0000",
|
||||
hub_emoji: "🏭",
|
||||
external_id: id,
|
||||
token: Livebook.Utils.random_cookie(),
|
||||
url: "http://localhost"
|
||||
|
|
Loading…
Add table
Reference in a new issue