Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,12 @@ and this project adheres to

### Changed

- Consolidated run and work order state definitions into single source of truth
by adding `Run.active_states/0`, `WorkOrder.states/0`, and
`WorkOrder.active_states/0` and replacing all hardcoded state lists across the
codebase
[#4589](https://github.com/OpenFn/lightning/issues/4589)

### Fixed

## [2.16.1-pre1] - 2026-04-04
Expand Down
17 changes: 11 additions & 6 deletions lib/lightning/dashboard_stats.ex
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ defmodule Lightning.DashboardStats do
alias Lightning.Workflows.Workflow
alias Lightning.WorkOrder

@wo_active WorkOrder.active_states()
@run_active Run.active_states()
@days_back 30

defmodule WorkflowStats do
Expand Down Expand Up @@ -58,7 +60,10 @@ defmodule Lightning.DashboardStats do
last_workorders = batch_get_last_workorders(workflow_ids)

last_failed_workorders =
batch_get_last_workorders(workflow_ids, [:pending, :running, :success])
batch_get_last_workorders(
workflow_ids,
WorkOrder.active_states() ++ [:success]
)
Comment on lines 62 to +66
Copy link

Copilot AI Apr 4, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

get_last_failed_workorder/2 later in this module still hardcodes [:pending, :running, :success] as excluded_states, which conflicts with the PR goal of removing hardcoded work order state lists from lib/. Consider deriving this list from WorkOrder.active_states() (or @wo_active) to keep a single source of truth.

Copilot uses AI. Check for mistakes.
Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed. Now deriving excluded_states from @wo_active ++ [:success], which matches the pattern already used in batch_get_last_workorders on line 65.


Enum.map(workflows, fn workflow ->
wf_id = workflow.id
Expand Down Expand Up @@ -192,7 +197,7 @@ defmodule Lightning.DashboardStats do
end

defp get_last_failed_workorder(workflow, %{state: :success}) do
excluded_states = [:pending, :running, :success]
excluded_states = @wo_active ++ [:success]
get_last_workorder(workflow, excluded_states)
end

Expand Down Expand Up @@ -229,7 +234,7 @@ defmodule Lightning.DashboardStats do
{:success, cnt}, acc ->
%{acc | success: cnt}

{state, cnt}, acc when state in [:pending, :running] ->
{state, cnt}, acc when state in @wo_active ->
Map.update!(acc, :pending, &(&1 + cnt))

{_other, cnt}, acc ->
Expand All @@ -251,7 +256,7 @@ defmodule Lightning.DashboardStats do
{:success, cnt}, acc ->
%{acc | success: cnt}

{state, cnt}, acc when state in [:available, :claimed, :started] ->
{state, cnt}, acc when state in @run_active ->
Map.update!(acc, :pending, &(&1 + cnt))

{_other, cnt}, acc ->
Expand Down Expand Up @@ -333,7 +338,7 @@ defmodule Lightning.DashboardStats do
:success ->
%{current | success: cnt}

s when s in [:pending, :running] ->
s when s in @wo_active ->
Map.update!(current, :pending, &(&1 + cnt))

_ ->
Expand Down Expand Up @@ -361,7 +366,7 @@ defmodule Lightning.DashboardStats do
:success ->
%{current | success: cnt}

s when s in [:available, :claimed, :started] ->
s when s in @run_active ->
Map.update!(current, :pending, &(&1 + cnt))

_ ->
Expand Down
4 changes: 3 additions & 1 deletion lib/lightning/runs/prom_ex_plugin/impeded_project_helper.ex
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,11 @@ defmodule Lightning.Runs.PromExPlugin.ImpededProjectHelper do
select: w.workflow_id,
distinct: true

in_progress = Lightning.Run.active_states() -- [:available]

in_progress_runs_query =
from r in Lightning.Run,
where: r.state in [:claimed, :started],
where: r.state in ^in_progress,
join: w in assoc(r, :work_order),
group_by: w.workflow_id,
select: %{workflow_id: w.workflow_id, count: count(r.id)}
Expand Down
2 changes: 1 addition & 1 deletion lib/lightning/runs/query.ex
Original file line number Diff line number Diff line change
Expand Up @@ -231,7 +231,7 @@ defmodule Lightning.Runs.Query do
# Step 1: Rank runs within each workflow by priority and insertion time
ranked_runs_query =
from(r in Run,
where: r.state in [:available, :claimed, :started],
where: r.state in ^Run.active_states(),
join: wo in assoc(r, :work_order),
join: w in assoc(wo, :workflow),
join: p in assoc(w, :project)
Expand Down
19 changes: 10 additions & 9 deletions lib/lightning/runs/run.ex
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,8 @@ defmodule Lightning.Run do
:final_dataclip_id
]}

@active_states [:available, :claimed, :started]

@final_states [
:success,
:failed,
Expand All @@ -56,6 +58,13 @@ defmodule Lightning.Run do
"""
def final_states, do: @final_states

@doc """
Returns the list of active (in-progress) states for a run.

These are all non-final states: available, claimed, and started.
"""
def active_states, do: @active_states

Comment on lines +61 to +67
Copy link

Copilot AI Apr 4, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Run.active_states/0 duplicates the same non-final state list that is also embedded in the field :state, Ecto.Enum, values: ... definition below. To avoid these drifting apart (and to better support the “single source of truth” goal), consider introducing an @active_states module attribute and using it both in active_states/0 and in the enum values definition.

Copilot uses AI. Check for mistakes.
Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Addressed. Introduced @active_states module attribute and wired it into both active_states/0 and the Ecto.Enum values definition, so the list only lives in one place now.

@doc """
Returns the list of failure states for a run.

Expand Down Expand Up @@ -98,15 +107,7 @@ defmodule Lightning.Run do
embeds_one :options, Lightning.Runs.RunOptions

field :state, Ecto.Enum,
values:
Enum.concat(
[
:available,
:claimed,
:started
],
@final_states
),
values: Enum.concat(@active_states, @final_states),
default: :available

field :error_type, :string
Expand Down
3 changes: 2 additions & 1 deletion lib/lightning/workorders/search_params.ex
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,8 @@ defmodule Lightning.WorkOrders.SearchParams do
:sort_direction
]}

@statuses ~w(pending running success failed crashed killed cancelled lost exception rejected)
@statuses Lightning.WorkOrder.states()
|> Enum.map(&Atom.to_string/1)
@statuses_set MapSet.new(@statuses)
@search_fields ~w(id body log dataclip_name)

Expand Down
14 changes: 13 additions & 1 deletion lib/lightning/workorders/workorder.ex
Original file line number Diff line number Diff line change
Expand Up @@ -21,11 +21,23 @@ defmodule Lightning.WorkOrder do
workflow: Workflow.t() | Ecto.Association.NotLoaded.t()
}

@active_states [:pending, :running]

@state_values Enum.concat(
[:rejected, :pending, :running],
[:rejected | @active_states],
Run.final_states()
)

@doc """
Returns all valid work order states.
"""
def states, do: @state_values

@doc """
Returns the list of active (in-progress) work order states.
"""
def active_states, do: @active_states

@derive {Jason.Encoder,
only: [
:id,
Expand Down
3 changes: 2 additions & 1 deletion lib/lightning_web/live/workflow_live/dashboard_components.ex
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ defmodule LightningWeb.WorkflowLive.DashboardComponents do

alias Lightning.DashboardStats.ProjectMetrics
alias Lightning.Projects.Project
alias Lightning.WorkOrder
alias Lightning.WorkOrders.SearchParams
alias LightningWeb.Components.Common
alias LightningWeb.WorkflowLive.Helpers
Expand Down Expand Up @@ -455,7 +456,7 @@ defmodule LightningWeb.WorkflowLive.DashboardComponents do
<div>
<div class="flex items-center gap-x-2">
<span class="relative inline-flex h-2 w-2">
<%= if @state in [:pending, :running] do %>
<%= if @state in WorkOrder.active_states() do %>
<span class={[
"animate-ping absolute inline-flex h-full w-full rounded-full opacity-75",
@dot_color
Expand Down
2 changes: 1 addition & 1 deletion test/lightning/digest_email_worker_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -153,7 +153,7 @@ defmodule Lightning.DigestEmailWorkerTest do
workflow = insert(:simple_workflow, project: project)

# Create workorders in non-final states (these should not be counted as failed)
create_runs(workflow, [:pending, :running])
create_runs(workflow, Lightning.WorkOrder.active_states())

# Create one actual failed run for comparison
create_runs(workflow, [:failed])
Expand Down
2 changes: 1 addition & 1 deletion test/lightning/janitor_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -161,7 +161,7 @@ defmodule Lightning.JanitorTest do

unfinished_runs_with_unfinished_steps =
Enum.map(
[:available, :claimed, :started],
Lightning.Run.active_states(),
fn state ->
insert(:run,
work_order: work_order,
Expand Down