Skip to content

Commit

Permalink
Implement basic IntrinsicFunctions
Browse files Browse the repository at this point in the history
  • Loading branch information
agrare committed Aug 8, 2023
1 parent c772c6c commit 4488d24
Show file tree
Hide file tree
Showing 6 changed files with 150 additions and 1 deletion.
2 changes: 2 additions & 0 deletions lib/floe.rb
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@
require_relative "floe/workflow/choice_rule/and"
require_relative "floe/workflow/choice_rule/data"
require_relative "floe/workflow/context"
require_relative "floe/workflow/intrinsic_function"
require_relative "floe/workflow/intrinsic_functions/states/string_to_json"
require_relative "floe/workflow/path"
require_relative "floe/workflow/payload_template"
require_relative "floe/workflow/reference_path"
Expand Down
55 changes: 55 additions & 0 deletions lib/floe/workflow/intrinsic_function.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
# frozen_string_literal: true

module Floe
class Workflow
class IntrinsicFunction
class << self
def klass(payload)
function_module, function =
payload.match(/^(?<module>\w+)\.(?<function>\w+)\(.*\)$/)
.named_captures.values_at("module", "function")

case "#{function_module}.#{function}"
when "States.StringToJson"
Floe::Workflow::IntrinsicFunctions::States::StringToJson
else
raise NotImplementedError
end
end

def value(payload, context, input = {})
new(payload).value(context, input)
end

private alias_method :orig_new, :new

def new(*args)
if self == Floe::Workflow::IntrinsicFunction
klass(*args).new(*args)
else
orig_new(*args)
end
end
end

attr_reader :args

def initialize(payload)
args = payload.match(/^\w+\.\w+\((.*)\)$/).captures.first
@args = args.split(", ").map do |arg|
if arg.start_with?("$.")
Path.new(arg)
elsif arg.match?(/^\w+\.\w+\(.*\)$/)
Floe::Workflow::IntrinsicFunction.new(arg)
else
arg
end
end
end

def value(_context, _inputs)
raise NotImplementedError, "must be implemented in a subclass"
end
end
end
end
23 changes: 23 additions & 0 deletions lib/floe/workflow/intrinsic_functions/states/string_to_json.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# frozen_string_literal: true

module Floe
class Workflow
module IntrinsicFunctions
module States
class StringToJson < Floe::Workflow::IntrinsicFunction
def value(context, inputs)
arg = args.first
arg =
if arg.kind_of?(Floe::Workflow::Path) || arg.kind_of?(Floe::Workflow::IntrinsicFunction)
arg.value(context, inputs)
else
arg
end

JSON.parse(arg)
end
end
end
end
end
end
4 changes: 3 additions & 1 deletion lib/floe/workflow/payload_template.rb
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,8 @@ def parse_payload(value)
when String
if value.start_with?("$")
Path.new(value)
elsif value.match?(/^\w+\.\w+\(.+\)$/)
IntrinsicFunction.new(value)
else
value
end
Expand All @@ -55,7 +57,7 @@ def interpolate_value(value, context, inputs)
[key, val]
end
end
when Path
when Path, IntrinsicFunction
value.value(context, inputs)
else
value
Expand Down
55 changes: 55 additions & 0 deletions spec/workflow/intrinsic_function_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
RSpec.describe Floe::Workflow::IntrinsicFunction do
describe ".klass" do
it "returns the intrinsic function class" do
payload = "States.StringToJson('{\"number\": 20}')"
expect(described_class.klass(payload)).to eq(Floe::Workflow::IntrinsicFunctions::States::StringToJson)
end

it "raises an exception for an invalid intrinsic function" do
payload = "States.MyFirstFunction()"
expect { described_class.klass(payload) }.to raise_error(NotImplementedError)
end
end

describe "#initialize" do
let(:payload) { "States.StringToJson()" }

it "returns an instance of the intrinsic function" do
expect(described_class.new(payload)).to be_kind_of(Floe::Workflow::IntrinsicFunctions::States::StringToJson)
end

context "with no arguments" do
it "parses args as empty" do
function = described_class.new(payload)
expect(function.args).to be_empty
end
end

context "with a string arguments" do
let(:payload) { "States.StringToJson(foobar)" }

it "parses the arguments" do
function = described_class.new(payload)
expect(function.args).to eq(["foobar"])
end
end

context "with a Path" do
let(:payload) { "States.StringToJson($.someString)" }

it "parses the arguments" do
function = described_class.new(payload)
expect(function.args.first).to be_kind_of(Floe::Workflow::Path)
end
end

context "with an IntrinsicFunction" do
let(:payload) { "States.StringToJson(States.StringToJson($.someString))" }

it "parses the arguments" do
function = described_class.new(payload)
expect(function.args.first).to be_kind_of(Floe::Workflow::IntrinsicFunction)
end
end
end
end
12 changes: 12 additions & 0 deletions spec/workflow/payload_template_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -38,5 +38,17 @@
expect(subject.value(context, inputs)).to eq({"foo" => ["bar", "baz"], "bar" => {"hello" => "world"}})
end
end

context "with intrinsic functions" do
context "States.StringToJson" do
let(:context) { {} }
let(:payload) { {"foo.$" => "States.StringToJson($.someString)"} }
let(:inputs) { {"someString" => "{\"number\": 20}"} }

it "sets foo to the parsed JSON from inputs" do
expect(subject.value(context, inputs)).to eq({"foo" => {"number" => 20}})
end
end
end
end
end

0 comments on commit 4488d24

Please sign in to comment.