Skip to content
Merged
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
20 changes: 18 additions & 2 deletions app/jobs/r3x/run_workflow_job.rb
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,13 @@ def perform(workflow_key, options = nil)

ctx = WorkflowContext.new(
trigger: execution,
workflow_key: workflow_key
workflow_key: workflow_key,
workflow_class: workflow_class
)

workflow_class.new.run(ctx)
guard_network_access(workflow_class) do
workflow_class.new.run(ctx)
end
end

private
Expand Down Expand Up @@ -61,5 +64,18 @@ def find_trigger(workflow_class:, trigger_key:)

trigger
end

def guard_network_access(workflow_class)
if workflow_class.uses?(:networking)
yield
else
begin
WebMock.disable_net_connect!
yield
ensure
WebMock.allow_net_connect!
end
end
end
end
end
26 changes: 0 additions & 26 deletions app/lib/r3x/client/discord_webhook.rb

This file was deleted.

23 changes: 23 additions & 0 deletions lib/r3x/workflow.rb
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
module R3x
class Workflow
KNOWN_CAPABILITIES = Set.new(%i[networking filesystem shell]).freeze

class << self
def inherited(subclass)
super
subclass._triggers = TriggerCollection.new
subclass._capabilities = Set.new
end

def workflow_key
Expand All @@ -16,6 +19,25 @@ def trigger(type, **options)
_triggers.add(trigger_instance)
end

def uses(*capabilities)
incoming = Set.new(capabilities.flatten.compact.map(&:to_sym))
unknown = incoming - KNOWN_CAPABILITIES
raise ArgumentError, "Unknown capabilities: #{unknown.to_a.join(', ')}. Known: #{KNOWN_CAPABILITIES.to_a.join(', ')}" if unknown.any?

duplicates = incoming & _capabilities
raise ArgumentError, "Capability already declared: #{duplicates.to_a.join(', ')}" if duplicates.any?

_capabilities.merge(incoming)
end

def capabilities
_capabilities.dup
end

def uses?(capability)
_capabilities.include?(capability.to_sym)
end

def triggers
_triggers.to_a
end
Expand All @@ -29,6 +51,7 @@ def triggers_by_key
end

attr_accessor :_triggers
attr_accessor :_capabilities
end

def run(ctx)
Expand Down
23 changes: 21 additions & 2 deletions lib/r3x/workflow_context.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,30 @@ module R3x
class WorkflowContext
include R3x::Concerns::Logger

attr_reader :trigger, :execution
attr_reader :trigger, :execution, :workflow_class

def initialize(trigger:, workflow_key:)
def initialize(trigger:, workflow_key:, workflow_class: nil)
@trigger = trigger
@workflow_class = workflow_class
@execution = WorkflowExecution.new(workflow_key: workflow_key)
end

def client
@client ||= ClientProxy.new(workflow_class: workflow_class)
end

class ClientProxy
def initialize(workflow_class:)
@workflow_class = workflow_class
end

def http
R3x::Client::Http.new
end

private

attr_reader :workflow_class
end
end
end
5 changes: 3 additions & 2 deletions lib/tasks/r3x_workflows.rake
Original file line number Diff line number Diff line change
Expand Up @@ -52,11 +52,12 @@ namespace :r3x do
puts "Running workflow: #{key}"
begin
workflow_class = R3x::WorkflowRegistry.fetch(key)
result = workflow_class.new.run(R3x::WorkflowContext.new)
trigger = workflow_class.triggers.find(&:manual?) || R3x::Triggers::Manual.new
result = RunWorkflowJob.new.perform(key, trigger_key: trigger.unique_key)
puts " ✓ Success: #{result.inspect}"
rescue => e
puts " ✗ Error: #{e.message}"
raise e if args[:workflow_key].present? # Re-raise if specific workflow failed
raise e if args[:workflow_key].present?
end
end

Expand Down
1 change: 1 addition & 0 deletions test/jobs/r3x/run_workflow_job_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ class RunWorkflowJobTest < ActiveSupport::TestCase

teardown do
ENV["R3X_WORKFLOW_PATHS"] = @original_workflow_paths
WebMock.reset!
end

test "performs workflow with manual trigger" do
Expand Down
4 changes: 2 additions & 2 deletions test/lib/r3x/client/hashi_corp_vault_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ class HashiCorpVaultTest < ActiveSupport::TestCase
body: {
data: {
data: {
discord_webhook_url: "https://discord.test",
api_key: "test-api-key",
mode: "real"
},
metadata: { version: 1 }
Expand All @@ -40,7 +40,7 @@ class HashiCorpVaultTest < ActiveSupport::TestCase
result = HashiCorpVault.read("secret/data/env/r3x")

assert_equal({
"discord_webhook_url" => "https://discord.test",
"api_key" => "test-api-key",
"mode" => "real"
}, result)
end
Expand Down
46 changes: 45 additions & 1 deletion test/lib/r3x/workflow_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -93,14 +93,58 @@ def self.name
def self.name
"Test"
end
trigger :schedule, cron: "0 13 * * *"
trigger :schedule, cron: "0 * * * *"
end

triggers = klass.triggers
assert_equal 1, triggers.size
assert_equal [ :schedule ], triggers.map(&:type)
end

test "uses declares workflow capabilities" do
klass = Class.new(R3x::Workflow) do
def self.name
"Workflows::NetworkedWorkflow"
end

uses :networking
end

assert_equal Set.new([ :networking ]), klass.capabilities
assert klass.uses?(:networking)
refute klass.uses?(:filesystem)
end

test "uses raises on duplicate capability" do
klass = Class.new(R3x::Workflow) do
def self.name
"Workflows::DuplicateCap"
end

uses :networking
end

error = assert_raises(ArgumentError) do
klass.uses(:networking)
end

assert_match "Capability already declared", error.message
end

test "uses raises on unknown capability" do
error = assert_raises(ArgumentError) do
Class.new(R3x::Workflow) do
def self.name
"Workflows::BadCap"
end

uses :hacking
end
end

assert_match "Unknown capabilities: hacking", error.message
end

test "trigger :schedule rejects blank cron (empty string and whitespace)" do
[
"",
Expand Down
2 changes: 2 additions & 0 deletions test/test_helper.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
require "rails/test_help"
require "webmock/minitest"

WebMock.disable_net_connect!

module ActiveSupport
class TestCase
# Run tests in parallel with specified workers
Expand Down