Update billing status handling (#2921)

This commit is contained in:
Wojtek Mach 2025-02-03 16:10:59 +01:00 committed by GitHub
parent 8fc98c7698
commit 08a0f1e14a
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
16 changed files with 163 additions and 22 deletions

View file

@ -8,7 +8,6 @@ defmodule Livebook.EctoTypes.HexColor do
def load(value), do: {:ok, value}
@impl true
def dump(value), do: {:ok, value}
@impl true

View file

@ -41,7 +41,7 @@ defmodule Livebook.Hubs.Team do
field :session_token, :string, redact: true
field :hub_name, :string
field :hub_emoji, :string
field :disabled, :boolean, default: false
field :billing_status, :map, default: %{disabled: false, type: nil}
embeds_one :offline, Offline
end

View file

@ -801,16 +801,30 @@ defmodule Livebook.Hubs.TeamClient do
state
end
defp update_hub(state, %LivebookProto.UserConnected{org_disabled: disabled}) do
update_hub(state, &put_in(&1.disabled, disabled))
defp update_hub(state, %LivebookProto.UserConnected{billing_status: billing_status}) do
update_hub(
state,
&put_billing_status(&1, billing_status)
)
end
defp update_hub(state, %LivebookProto.OrgUpdated{disabled: disabled}) do
update_hub(state, &put_in(&1.disabled, disabled))
defp update_hub(state, %LivebookProto.AgentConnected{
public_key: org_public_key,
billing_status: billing_status
}) do
update_hub(
state,
&(&1
|> struct!(org_public_key: org_public_key)
|> put_billing_status(billing_status))
)
end
defp update_hub(state, %LivebookProto.AgentConnected{public_key: org_public_key}) do
update_hub(state, &put_in(&1.org_public_key, org_public_key))
defp update_hub(state, %LivebookProto.OrgUpdated{billing_status: billing_status}) do
update_hub(
state,
&put_billing_status(&1, billing_status)
)
end
defp update_hub(state, fun) when is_function(fun, 1) do
@ -823,6 +837,41 @@ defmodule Livebook.Hubs.TeamClient do
put_in(state.hub, hub)
end
defp put_billing_status(hub, %LivebookProto.BillingStatus{} = status) do
put_in(
hub.billing_status,
Map.merge(%{disabled: status.disabled}, put_billing_status(status.type))
)
end
# TODO: Remove when Billign is public
defp put_billing_status(hub, nil) do
put_in(
hub.billing_status,
%{disabled: false, type: nil}
)
end
defp put_billing_status({:trialing, %LivebookProto.BillingStatusTrialing{} = type}) do
%{type: :trialing, trial_ends_at: DateTime.from_unix!(type.trial_ends_at)}
end
defp put_billing_status({:trial_ended, %LivebookProto.BillingStatusTrialEnded{} = type}) do
%{type: :trial_ended, trial_ends_at: DateTime.from_unix!(type.trial_ends_at)}
end
defp put_billing_status({:canceling, %LivebookProto.BillingStatusCanceling{} = type}) do
%{type: :canceling, cancel_at: DateTime.from_unix!(type.cancel_at)}
end
defp put_billing_status({:canceled, %LivebookProto.BillingStatusCanceled{} = type}) do
%{type: :canceled, cancel_at: DateTime.from_unix!(type.cancel_at)}
end
defp put_billing_status(_other) do
%{type: nil}
end
defp diff(old_list, new_list, fun, deleted_fun \\ nil, updated_fun \\ nil) do
deleted_fun = unless deleted_fun, do: fun, else: deleted_fun
updated_fun = unless updated_fun, do: fun, else: updated_fun

View file

@ -63,7 +63,20 @@ defmodule LivebookWeb.Hub.Edit.TeamComponent do
{Provider.connection_status(@hub)}
</LayoutComponents.topbar>
<LayoutComponents.topbar :if={@hub.disabled} variant="warning">
<LayoutComponents.topbar :if={@hub.billing_status.type == :trialing} variant="warning">
<h2>
Your organization has
<strong>
{Date.diff(@hub.billing_status.trial_ends_at, Date.utc_today())} day(s) left
</strong>
on the free trial. Need help getting set up? <a
class="underline"
href="mailto:suport@livebook.dev?subject=Help%20with%20Livebook%20Teams"
>Contact us</a>.
</h2>
</LayoutComponents.topbar>
<LayoutComponents.topbar :if={@hub.billing_status.disabled} variant="warning">
<h2>
Workspace disabled: your organization doesn't have an active subscription. Please contact your <.link
href={org_url(@hub, "/users")}
@ -185,14 +198,14 @@ defmodule LivebookWeb.Hub.Edit.TeamComponent do
secrets={@secrets}
edit_path={"hub/#{@hub.id}/secrets/edit"}
return_to={~p"/hub/#{@hub.id}"}
disabled={@hub.disabled}
disabled={@hub.billing_status.disabled}
/>
<div>
<.button
patch={~p"/hub/#{@hub.id}/secrets/new"}
id="add-secret"
disabled={@hub.disabled}
disabled={@hub.billing_status.disabled}
>
Add secret
</.button>
@ -214,7 +227,7 @@ defmodule LivebookWeb.Hub.Edit.TeamComponent do
hub_id={@hub.id}
file_systems={@file_systems}
target={@myself}
disabled={@hub.disabled}
disabled={@hub.billing_status.disabled}
/>
</div>
@ -251,7 +264,7 @@ defmodule LivebookWeb.Hub.Edit.TeamComponent do
<.button
patch={~p"/hub/#{@hub.id}/groups/new"}
id="add-deployment-group"
disabled={@hub.disabled}
disabled={@hub.billing_status.disabled}
>
Add deployment group
</.button>
@ -308,7 +321,7 @@ defmodule LivebookWeb.Hub.Edit.TeamComponent do
secret_name={@secret_name}
secret_value={@secret_value}
return_to={~p"/hub/#{@hub.id}"}
disabled={@hub.disabled}
disabled={@hub.billing_status.disabled}
/>
</.modal>
@ -323,7 +336,7 @@ defmodule LivebookWeb.Hub.Edit.TeamComponent do
module={LivebookWeb.Hub.FileSystemFormComponent}
id="file-systems"
hub={@hub}
disabled={@hub.disabled}
disabled={@hub.billing_status.disabled}
file_system={@file_system}
file_system_id={@file_system_id}
return_to={~p"/hub/#{@hub.id}"}

View file

@ -157,7 +157,7 @@ defmodule LivebookWeb.Hub.Teams.DeploymentGroupComponent do
secret_name={@secret_name}
secret_value={@secret_value}
return_to={~p"/hub/#{@hub.id}"}
disabled={@hub.disabled}
disabled={@hub.billing_status.disabled}
/>
</.modal>

View file

@ -18,5 +18,5 @@ defmodule LivebookProto.AgentConnected do
json_name: "appDeployments"
field :agents, 9, repeated: true, type: LivebookProto.Agent
field :org_disabled, 10, type: :bool, json_name: "orgDisabled"
field :billing_status, 10, type: LivebookProto.BillingStatus, json_name: "billingStatus"
end

View file

@ -0,0 +1,16 @@
defmodule LivebookProto.BillingStatus do
use Protobuf, protoc_gen_elixir_version: "0.13.0", syntax: :proto3
oneof :type, 0
field :disabled, 1, type: :bool
field :trialing, 2, type: LivebookProto.BillingStatusTrialing, oneof: 0
field :trial_ended, 3,
type: LivebookProto.BillingStatusTrialEnded,
json_name: "trialEnded",
oneof: 0
field :canceling, 4, type: LivebookProto.BillingStatusCanceling, oneof: 0
field :canceled, 5, type: LivebookProto.BillingStatusCanceled, oneof: 0
end

View file

@ -0,0 +1,5 @@
defmodule LivebookProto.BillingStatusCanceled do
use Protobuf, protoc_gen_elixir_version: "0.13.0", syntax: :proto3
field :cancel_at, 1, type: :int64, json_name: "cancelAt"
end

View file

@ -0,0 +1,5 @@
defmodule LivebookProto.BillingStatusCanceling do
use Protobuf, protoc_gen_elixir_version: "0.13.0", syntax: :proto3
field :cancel_at, 1, type: :int64, json_name: "cancelAt"
end

View file

@ -0,0 +1,5 @@
defmodule LivebookProto.BillingStatusTrialEnded do
use Protobuf, protoc_gen_elixir_version: "0.13.0", syntax: :proto3
field :trial_ends_at, 1, type: :int64, json_name: "trialEndsAt"
end

View file

@ -0,0 +1,5 @@
defmodule LivebookProto.BillingStatusTrialing do
use Protobuf, protoc_gen_elixir_version: "0.13.0", syntax: :proto3
field :trial_ends_at, 1, type: :int64, json_name: "trialEndsAt"
end

View file

@ -0,0 +1,9 @@
defmodule LivebookProto.OrgStatusCancel do
@moduledoc """
the org is or will be cancelled
"""
use Protobuf, protoc_gen_elixir_version: "0.13.0", syntax: :proto3
field :cancel_at, 1, type: :int64, json_name: "cancelAt"
end

View file

@ -0,0 +1,9 @@
defmodule LivebookProto.OrgStatusTrial do
@moduledoc """
the org is on active or expired trial
"""
use Protobuf, protoc_gen_elixir_version: "0.13.0", syntax: :proto3
field :trial_ends_at, 1, type: :int64, json_name: "trialEndsAt"
end

View file

@ -2,5 +2,5 @@ defmodule LivebookProto.OrgUpdated do
use Protobuf, protoc_gen_elixir_version: "0.13.0", syntax: :proto3
field :id, 1, type: :string
field :disabled, 2, type: :bool
field :billing_status, 3, type: LivebookProto.BillingStatus, json_name: "billingStatus"
end

View file

@ -16,5 +16,5 @@ defmodule LivebookProto.UserConnected do
json_name: "appDeployments"
field :agents, 6, repeated: true, type: LivebookProto.Agent
field :org_disabled, 7, type: :bool, json_name: "orgDisabled"
field :billing_status, 7, type: LivebookProto.BillingStatus, json_name: "billingStatus"
end

View file

@ -110,7 +110,7 @@ message UserConnected {
repeated DeploymentGroup deployment_groups = 4;
repeated AppDeployment app_deployments = 5;
repeated Agent agents = 6;
bool org_disabled = 7;
BillingStatus billing_status = 7;
}
message AgentConnected {
@ -122,7 +122,7 @@ message AgentConnected {
repeated DeploymentGroup deployment_groups = 7;
repeated AppDeployment app_deployments = 8;
repeated Agent agents = 9;
bool org_disabled = 10;
BillingStatus billing_status = 10;
}
message AppDeployment {
@ -161,7 +161,7 @@ message AgentLeft {
message OrgUpdated {
string id = 1;
bool disabled = 2;
BillingStatus billing_status = 3;
}
message Agent {
@ -220,3 +220,29 @@ message Event {
OrgUpdated org_updated = 17;
}
}
message BillingStatus {
bool disabled = 1;
oneof type {
BillingStatusTrialing trialing = 2;
BillingStatusTrialEnded trial_ended = 3;
BillingStatusCanceling canceling = 4;
BillingStatusCanceled canceled = 5;
}
}
message BillingStatusTrialing {
int64 trial_ends_at = 1;
}
message BillingStatusTrialEnded {
int64 trial_ends_at = 1;
}
message BillingStatusCanceling {
int64 cancel_at = 1;
}
message BillingStatusCanceled {
int64 cancel_at = 1;
}