Packages
livebook
0.19.0
0.19.8
0.19.7
0.19.6
0.19.5
0.19.4
0.19.3
0.19.2
0.19.1
0.19.0
0.18.6
0.18.5
0.18.4
0.18.3
0.18.2
0.18.1
0.18.0
0.17.3
0.17.2
0.17.1
0.17.0
0.16.4
0.16.3
0.16.2
0.16.1
0.16.0
0.15.5
0.15.4
0.15.3
0.15.2
0.15.1
0.15.0
0.14.7
0.14.6
0.14.5
0.14.4
0.14.3
0.14.2
0.14.1
0.14.0
0.14.0-rc.1
0.14.0-rc.0
0.13.3
0.13.2
0.13.1
0.13.0
0.12.1
0.12.0
0.11.4
0.11.3
0.11.2
0.11.1
0.11.0
0.10.0
0.9.3
0.9.2
0.9.1
0.9.0
0.8.2
0.8.1
0.8.0
0.7.2
0.7.1
0.7.0
0.6.3
0.6.2
0.6.1
0.6.0
0.5.2
0.5.1
0.5.0
0.4.1
0.4.0
0.3.2
0.3.1
0.3.0
0.2.3
0.2.2
0.2.1
0.2.0
0.1.2
0.1.1
0.1.0
Automate code & data workflows with interactive notebooks
Current section
Files
Jump to
Current section
Files
lib/livebook/notebook/app_settings.ex
defmodule Livebook.Notebook.AppSettings do
# Data structure configuring how notebook gets deployed as an app.
use Ecto.Schema
import Ecto.Changeset, except: [change: 1, change: 2]
@type t :: %__MODULE__{
slug: String.t() | nil,
multi_session: boolean(),
zero_downtime: boolean(),
show_existing_sessions: boolean(),
auto_shutdown_ms: pos_integer() | nil,
access_type: access_type(),
password: String.t() | nil,
show_source: boolean(),
output_type: output_type(),
app_folder_id: String.t() | nil
}
@type access_type :: :public | :protected
@type output_type :: :all | :rich
@access_types [:public, :protected]
@primary_key false
embedded_schema do
field :slug, :string
field :multi_session, :boolean
field :zero_downtime, :boolean
field :show_existing_sessions, :boolean
field :auto_shutdown_ms, :integer
field :access_type, Ecto.Enum, values: @access_types
field :password, :string
field :show_source, :boolean
field :output_type, Ecto.Enum, values: [:all, :rich]
field :app_folder_id, :string
end
@doc """
Returns default app settings.
"""
@spec new() :: t()
def new() do
%__MODULE__{
slug: nil,
multi_session: false,
zero_downtime: false,
show_existing_sessions: false,
auto_shutdown_ms: nil,
access_type: :protected,
password: generate_password(),
show_source: false,
output_type: :all,
app_folder_id: nil
}
end
defp generate_password() do
:crypto.strong_rand_bytes(10) |> Base.encode32(case: :lower)
end
@doc """
Returns an `%Ecto.Changeset{}` for tracking settings changes.
"""
@spec change(t(), map()) :: Ecto.Changeset.t()
def change(%__MODULE__{} = settings, attrs \\ %{}) do
changeset(settings, attrs)
end
@doc """
Updates settings with the given changes.
"""
@spec update(t(), map()) :: {:ok, t()} | {:error, Ecto.Changeset.t()}
def update(%__MODULE__{} = settings, attrs) do
changeset = changeset(settings, attrs)
apply_action(changeset, :update)
end
defp changeset(settings, attrs) do
settings
|> cast(attrs, [
:slug,
:multi_session,
:auto_shutdown_ms,
:access_type,
:show_source,
:output_type,
:app_folder_id
])
|> validate_required([
:slug,
:multi_session,
:access_type,
:show_source,
:output_type
])
|> validate_format(:slug, ~r/^[a-z0-9-]+$/,
message: "should only contain lowercase alphanumeric characters and dashes"
)
|> cast_access_attrs(attrs)
|> cast_mode_specific_attrs(attrs)
|> put_defaults()
end
defp cast_access_attrs(changeset, attrs) do
case get_field(changeset, :access_type) do
:protected ->
changeset
|> cast(attrs, [:password])
|> validate_required([:password])
|> validate_length(:password, min: 12)
_other ->
changeset
end
end
defp cast_mode_specific_attrs(changeset, attrs) do
case get_field(changeset, :multi_session) do
false ->
changeset
|> cast(attrs, [:zero_downtime])
|> validate_required([:zero_downtime])
# Listing sessions is not applicable to single-session apps,
# since they have a single session at a time. We reset to
# the default, so we do not persist it unnecessarily
|> put_change(:show_existing_sessions, false)
true ->
changeset
|> cast(attrs, [:show_existing_sessions])
|> validate_required([:show_existing_sessions])
# Zero-downtime deployment is not applicable to multi-session
# apps, since they are inherently zero-downtime. We reset to
# the default, so we do not persist it unnecessarily
|> put_change(:zero_downtime, false)
end
end
defp put_defaults(changeset) do
case get_change(changeset, :multi_session) do
nil ->
changeset
false ->
changeset
|> put_change(:auto_shutdown_ms, nil)
true ->
changeset
|> put_change(:auto_shutdown_ms, 5_000)
end
end
@doc """
Checks if the app settings are complete and valid.
"""
@spec valid?(t()) :: boolean()
def valid?(settings) do
change(settings).valid?
end
@doc """
Returns a list of possible access types.
"""
@spec access_types() :: list(atom())
def access_types(), do: @access_types
end