Skip to content

Commit

Permalink
Limit retention period (#2618)
Browse files Browse the repository at this point in the history
* Add Extension for limiting data retention periods

* allow message to be nil

* remove placeholder text

* make credo happy

* update changelog

* centralize data retention options
  • Loading branch information
midigofrank authored Oct 31, 2024
1 parent 0f1fd67 commit 5ef1aca
Show file tree
Hide file tree
Showing 24 changed files with 263 additions and 60 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@ and this project adheres to

- Enforcing MFA for a project can be enforced by the usage limiter
[#2607](https://github.com/OpenFn/lightning/pull/2607)
- Add extensions for limiting retention period
[#2618](https://github.com/OpenFn/lightning/pull/2618)

### Fixed

Expand Down
4 changes: 4 additions & 0 deletions lib/lightning/extensions/project_hook.ex
Original file line number Diff line number Diff line change
Expand Up @@ -42,4 +42,8 @@ defmodule Lightning.Extensions.ProjectHook do

Repo.delete(project)
end

@spec handle_project_validation(Changeset.t(Project.t())) ::
Changeset.t(Project.t())
def handle_project_validation(changeset), do: changeset
end
3 changes: 3 additions & 0 deletions lib/lightning/extensions/project_hooking.ex
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,7 @@ defmodule Lightning.Extensions.ProjectHooking do

@callback handle_delete_project(Project.t()) ::
{:ok, Project.t()} | {:error, Changeset.t()}

@callback handle_project_validation(Ecto.Changeset.t(Project.t())) ::
Ecto.Changeset.t(Project.t())
end
8 changes: 8 additions & 0 deletions lib/lightning/extensions/usage_limiter.ex
Original file line number Diff line number Diff line change
Expand Up @@ -20,4 +20,12 @@ defmodule Lightning.Extensions.UsageLimiter do
run_timeout_ms: Lightning.Config.default_max_run_duration() * 1000
]
end

@impl true
def get_data_retention_periods(_context) do
Lightning.Projects.Project.data_retention_options()
end

@impl true
def get_data_retention_message(_context), do: nil
end
7 changes: 7 additions & 0 deletions lib/lightning/extensions/usage_limiting.ex
Original file line number Diff line number Diff line change
Expand Up @@ -89,4 +89,11 @@ defmodule Lightning.Extensions.UsageLimiting do
"""
@callback get_run_options(context :: Context.t()) ::
Lightning.Runs.RunOptions.keyword_list()

@callback get_data_retention_periods(context :: Context.t()) :: [
pos_integer(),
...
]

@callback get_data_retention_message(context :: Context.t()) :: message() | nil
end
4 changes: 2 additions & 2 deletions lib/lightning/pipeline/failure_alerter.ex
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ defmodule Lightning.FailureAlerter do

use LightningWeb, :verified_routes

alias Lightning.Projects.ProjectAlertsLimiter
alias Lightning.Projects.ProjectLimiter
alias Lightning.Run

def alert_on_failure(nil), do: nil
Expand All @@ -14,7 +14,7 @@ defmodule Lightning.FailureAlerter do
def alert_on_failure(%Run{} = run) do
workflow = run.work_order.workflow

if :ok == ProjectAlertsLimiter.limit_failure_alert(workflow.project_id) do
if :ok == ProjectLimiter.limit_failure_alert(workflow.project_id) do
Lightning.Accounts.get_users_to_alert_for_project(%{
id: workflow.project_id
})
Expand Down
5 changes: 4 additions & 1 deletion lib/lightning/projects.ex
Original file line number Diff line number Diff line change
Expand Up @@ -287,7 +287,10 @@ defmodule Lightning.Projects do
"""
def update_project(%Project{} = project, attrs) do
changeset = Project.changeset(project, attrs)
changeset =
project
|> Project.changeset(attrs)
|> ProjectHook.handle_project_validation()

case Repo.update(changeset) do
{:ok, updated_project} ->
Expand Down
11 changes: 7 additions & 4 deletions lib/lightning/projects/project.ex
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,6 @@ defmodule Lightning.Projects.Project do

@type retention_policy_type :: :retain_all | :retain_with_errors | :erase_all

@retention_periods [7, 14, 30, 90, 180, 365]

schema "projects" do
field :name, :string
field :description, :string
Expand All @@ -45,6 +43,11 @@ defmodule Lightning.Projects.Project do
timestamps()
end

@spec data_retention_options() :: [pos_integer(), ...]
def data_retention_options do
[7, 14, 30, 90, 180, 365]
end

@doc false
# TODO: schedule_deletion shouldn't be changed by user input
def changeset(project, attrs) do
Expand All @@ -67,9 +70,9 @@ defmodule Lightning.Projects.Project do
|> validate_length(:description, max: 240)
|> validate_required([:name])
|> validate_format(:name, ~r/^[a-z\-\d]+$/)
|> validate_inclusion(:history_retention_period, @retention_periods)
|> validate_inclusion(:dataclip_retention_period, @retention_periods)
|> validate_dataclip_retention_period()
|> validate_inclusion(:history_retention_period, data_retention_options())
|> validate_inclusion(:dataclip_retention_period, data_retention_options())
end

defp validate_dataclip_retention_period(changeset) do
Expand Down
16 changes: 0 additions & 16 deletions lib/lightning/projects/project_alerts_limiter.ex

This file was deleted.

50 changes: 50 additions & 0 deletions lib/lightning/projects/project_limiter.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
defmodule Lightning.Projects.ProjectLimiter do
@moduledoc false

alias Lightning.Extensions.UsageLimiting
alias Lightning.Extensions.UsageLimiting.Action
alias Lightning.Extensions.UsageLimiting.Context
alias Lightning.Services.UsageLimiter

@spec limit_failure_alert(project_id :: Ecto.UUID.t()) ::
:ok | UsageLimiting.error()
def limit_failure_alert(project_id) do
UsageLimiter.limit_action(%Action{type: :alert_failure}, %Context{
project_id: project_id
})
end

@spec request_new_user(
project_id :: Ecto.UUID.t(),
user_count :: non_neg_integer()
) :: :ok | UsageLimiting.error()
def request_new_user(project_id, user_count) do
UsageLimiter.limit_action(
%Action{
type: :new_user,
amount: user_count
},
%Context{
project_id: project_id
}
)
end

@spec get_data_retention_periods(project_id :: Ecto.UUID.t()) :: [
pos_integer(),
...
]
def get_data_retention_periods(project_id) do
UsageLimiter.get_data_retention_periods(%Context{
project_id: project_id
})
end

@spec get_data_retention_message(project_id :: Ecto.UUID.t()) ::
Lightning.Extensions.Message.t()
def get_data_retention_message(project_id) do
UsageLimiter.get_data_retention_message(%Context{
project_id: project_id
})
end
end
24 changes: 0 additions & 24 deletions lib/lightning/projects/project_users_limiter.ex

This file was deleted.

6 changes: 6 additions & 0 deletions lib/lightning/services/project_hook.ex
Original file line number Diff line number Diff line change
Expand Up @@ -21,5 +21,11 @@ defmodule Lightning.Services.ProjectHook do
adapter().handle_delete_project(project)
end

@spec handle_project_validation(Changeset.t(Project.t())) ::
Changeset.t(Project.t())
def handle_project_validation(changeset) do
adapter().handle_project_validation(changeset)
end

defp adapter, do: adapter(:project_hook)
end
10 changes: 10 additions & 0 deletions lib/lightning/services/usage_limiter.ex
Original file line number Diff line number Diff line change
Expand Up @@ -26,5 +26,15 @@ defmodule Lightning.Services.UsageLimiter do
adapter().get_run_options(context)
end

@impl true
def get_data_retention_periods(context) do
adapter().get_data_retention_periods(context)
end

@impl true
def get_data_retention_message(context) do
adapter().get_data_retention_message(context)
end

defp adapter, do: adapter(:usage_limiter)
end
13 changes: 13 additions & 0 deletions lib/lightning_web/hooks.ex
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ defmodule LightningWeb.Hooks do
alias Lightning.Extensions.UsageLimiting.Context
alias Lightning.Policies.Permissions
alias Lightning.Policies.ProjectUsers
alias Lightning.Projects.ProjectLimiter
alias Lightning.Services.UsageLimiter
alias Lightning.VersionControl.VersionControlUsageLimiter

Expand Down Expand Up @@ -102,4 +103,16 @@ defmodule LightningWeb.Hooks do
{:cont, assign(socket, mfa_banner: component, can_require_mfa: false)}
end
end

def on_mount(:limit_retention_periods, _params, _session, socket) do
%{project: project} = socket.assigns
retention_periods = ProjectLimiter.get_data_retention_periods(project.id)
retention_message = ProjectLimiter.get_data_retention_message(project.id)

{:cont,
assign(socket,
data_retention_periods: retention_periods,
data_retention_limit_message: retention_message
)}
end
end
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ defmodule LightningWeb.ProjectLive.InviteCollaboratorComponent do
use LightningWeb, :live_component

alias Lightning.Projects
alias Lightning.Projects.ProjectUsersLimiter
alias Lightning.Projects.ProjectLimiter
alias LightningWeb.ProjectLive.InvitedCollaborators
alias Phoenix.LiveView.JS

Expand Down Expand Up @@ -91,7 +91,7 @@ defmodule LightningWeb.ProjectLive.InviteCollaboratorComponent do

project_users = Ecto.Changeset.get_embed(changeset, :invited_collaborators)

case ProjectUsersLimiter.request_new(
case ProjectLimiter.request_new_user(
socket.assigns.project.id,
Enum.count(project_users)
) do
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ defmodule LightningWeb.ProjectLive.NewCollaboratorComponent do
use LightningWeb, :live_component

alias Lightning.Projects
alias Lightning.Projects.ProjectUsersLimiter
alias Lightning.Projects.ProjectLimiter
alias LightningWeb.ProjectLive.Collaborators
alias Phoenix.LiveView.JS

Expand Down Expand Up @@ -130,7 +130,7 @@ defmodule LightningWeb.ProjectLive.NewCollaboratorComponent do

project_users = Ecto.Changeset.get_embed(changeset, :collaborators)

case ProjectUsersLimiter.request_new(
case ProjectLimiter.request_new_user(
socket.assigns.project.id,
Enum.count(project_users)
) do
Expand Down
8 changes: 4 additions & 4 deletions lib/lightning_web/live/project_live/settings.ex
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,8 @@ defmodule LightningWeb.ProjectLive.Settings do
alias Lightning.Policies.Permissions
alias Lightning.Policies.ProjectUsers
alias Lightning.Projects
alias Lightning.Projects.ProjectAlertsLimiter
alias Lightning.Projects.ProjectLimiter
alias Lightning.Projects.ProjectUser
alias Lightning.Projects.ProjectUsersLimiter
alias Lightning.VersionControl
alias Lightning.WebhookAuthMethods
alias Lightning.Workflows.WebhookAuthMethod
Expand All @@ -28,6 +27,7 @@ defmodule LightningWeb.ProjectLive.Settings do
on_mount {LightningWeb.Hooks, :project_scope}
on_mount {LightningWeb.Hooks, :limit_github_sync}
on_mount {LightningWeb.Hooks, :limit_mfa}
on_mount {LightningWeb.Hooks, :limit_retention_periods}

@impl true
def mount(_params, _session, socket) do
Expand Down Expand Up @@ -119,7 +119,7 @@ defmodule LightningWeb.ProjectLive.Settings do
)

can_receive_failure_alerts =
:ok == ProjectAlertsLimiter.limit_failure_alert(project.id)
:ok == ProjectLimiter.limit_failure_alert(project.id)

repo_connection = VersionControl.get_repo_connection_for_project(project.id)

Expand Down Expand Up @@ -685,7 +685,7 @@ defmodule LightningWeb.ProjectLive.Settings do
end

defp get_collaborator_limit_error(project) do
case ProjectUsersLimiter.request_new(project.id, 1) do
case ProjectLimiter.request_new_user(project.id, 1) do
:ok ->
nil

Expand Down
26 changes: 22 additions & 4 deletions lib/lightning_web/live/project_live/settings.html.heex
Original file line number Diff line number Diff line change
Expand Up @@ -898,19 +898,37 @@
</div>
</div>

<div class="">
<div class="flex gap-4 items-center">
<.input
type="select"
prompt="Select Period"
options={
Enum.map([7, 14, 30, 90, 180, 365], fn days ->
Enum.map(@data_retention_periods, fn days ->
{"#{days} Days", days}
end)
}
disabled={!@can_edit_data_retention}
disabled={
!@can_edit_data_retention ||
Enum.count(@data_retention_periods) == 1
}
field={f[:history_retention_period]}
class="h-4 w-4 border-gray-300 text-indigo-600 focus:ring-indigo-600"
/>
<div class="text-xs">
<%= case assigns[:data_retention_limit_message] do %>
<% %{function: func} when is_function(func) -> %>
<%= Phoenix.LiveView.TagEngine.component(
@data_retention_limit_message.function,
@data_retention_limit_message.attrs,
{__ENV__.module, __ENV__.function, __ENV__.file,
__ENV__.line}
) %>
<% %{text: text} when is_binary(text) -> %>
<span><%= text %></span>
<% _other -> %>
<span></span>
<% end %>
</div>
</div>
</div>
<div class="inset-0 flex items-center" aria-hidden="true">
Expand Down Expand Up @@ -993,7 +1011,7 @@
type="select"
prompt="Select Period"
options={
Enum.map([7, 14, 30, 90, 180, 365], fn days ->
Enum.map(@data_retention_periods, fn days ->
{"#{days} Days", days}
end)
}
Expand Down
Loading

0 comments on commit 5ef1aca

Please sign in to comment.