Skip to content

Commit

Permalink
Merge pull request #3280 from huginn/java_script_agent_supports_key_v…
Browse files Browse the repository at this point in the history
…alue_store_agent

JavaScriptAgent can access storage provided by KeyValueStoreAgents
  • Loading branch information
knu committed Jun 22, 2023
2 parents 28934d4 + 762fa64 commit bb29e25
Show file tree
Hide file tree
Showing 2 changed files with 80 additions and 22 deletions.
7 changes: 7 additions & 0 deletions app/models/agents/java_script_agent.rb
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ class JavaScriptAgent < Agent
* `this.options(key)`
* `this.log(message)`
* `this.error(message)`
* `this.kvs` (whose properties are variables provided by KeyValueStoreAgents)
* `this.escapeHtml(htmlToEscape)`
* `this.unescapeHtml(htmlToUnescape)`
MD
Expand Down Expand Up @@ -129,6 +130,12 @@ def execute_js(js_function, incoming_events = [])
context.attach('getCredential', ->(k) { credential(k); })
context.attach('setCredential', ->(k, v) { set_credential(k, v) })

kvs = Agents::KeyValueStoreAgent.merge(controllers).find_each.to_h { |kvs|
[kvs.options[:variable], kvs.memory.as_json]
}
context.attach("getKeyValueStores", -> { kvs })
context.eval("Object.defineProperty(Agent, 'kvs', { get: getKeyValueStores })")

if (options['language'] || '').downcase == 'coffeescript'
context.eval(CoffeeScript.compile(code))
else
Expand Down
95 changes: 73 additions & 22 deletions spec/models/agents/java_script_agent_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,9 @@
describe Agents::JavaScriptAgent do
before do
@valid_params = {
:name => "somename",
:options => {
:code => "Agent.check = function() { this.createEvent({ 'message': 'hi' }); };",
name: "somename",
options: {
code: "Agent.check = function() { this.createEvent({ 'message': 'hi' }); };",
}
}

Expand Down Expand Up @@ -42,7 +42,7 @@
expect(@agent).to be_valid
@agent.options['code'] = 'credential:foo'
expect(@agent).not_to be_valid
users(:jane).user_credentials.create! :credential_name => "foo", :credential_value => "bar"
users(:jane).user_credentials.create! credential_name: "foo", credential_value: "bar"
expect(@agent.reload).to be_valid
end
end
Expand Down Expand Up @@ -85,13 +85,14 @@
expect {
@agent.receive([events(:bob_website_agent_event)])
@agent.check
}.not_to change { AgentLog.count }
}.not_to(change { AgentLog.count })
}.to change { Event.count }.by(2)
end

describe "using credentials as code" do
before do
@agent.user.user_credentials.create :credential_name => 'code-foo', :credential_value => 'Agent.check = function() { this.log("ran it"); };'
@agent.user.user_credentials.create credential_name: 'code-foo',
credential_value: 'Agent.check = function() { this.log("ran it"); };'
@agent.options['code'] = "credential:code-foo\n\n"
@agent.save!
end
Expand Down Expand Up @@ -151,7 +152,7 @@
};'
@agent.save!
@agent.check
expect(@agent.memory['foo']).to eq([1,2])
expect(@agent.memory['foo']).to eq([1, 2])
@agent.save!
expect { @agent.reload.memory }.not_to raise_error
end
Expand All @@ -165,7 +166,7 @@
};'
@agent.save!
@agent.check
expect(@agent.memory['foo']).to eq({"one"=>1, "two"=> [1,2]})
expect(@agent.memory['foo']).to eq({ "one" => 1, "two" => [1, 2] })
@agent.save!
expect { @agent.reload.memory }.not_to raise_error
end
Expand All @@ -182,7 +183,7 @@
};'
@agent.save!
@agent.check
expect(@agent.memory['foo']).to eq({"three"=>3, "four"=>{"one"=>1, "two"=>2}})
expect(@agent.memory['foo']).to eq({ "three" => 3, "four" => { "one" => 1, "two" => 2 } })
@agent.save!
expect { @agent.reload.memory }.not_to raise_error
end
Expand Down Expand Up @@ -265,12 +266,13 @@

describe "creating events" do
it "creates events with this.createEvent in the JavaScript environment" do
@agent.options['code'] = 'Agent.check = function() { this.createEvent({ message: "This is an event!", stuff: { foo: 5 } }); };'
@agent.options['code'] =
'Agent.check = function() { this.createEvent({ message: "This is an event!", stuff: { foo: 5 } }); };'
@agent.save!
expect {
expect {
@agent.check
}.not_to change { AgentLog.count }
}.not_to(change { AgentLog.count })
}.to change { Event.count }.by(1)
created_event = @agent.events.last
expect(created_event.payload).to eq({ 'message' => "This is an event!", 'stuff' => { 'foo' => 5 } })
Expand Down Expand Up @@ -298,23 +300,25 @@

describe "escaping and unescaping HTML" do
it "can escape and unescape html with this.escapeHtml and this.unescapeHtml in the javascript environment" do
@agent.options['code'] = 'Agent.check = function() { this.createEvent({ escaped: this.escapeHtml(\'test \"escaping\" <characters>\'), unescaped: this.unescapeHtml(\'test &quot;unescaping&quot; &lt;characters&gt;\')}); };'
@agent.options['code'] =
'Agent.check = function() { this.createEvent({ escaped: this.escapeHtml(\'test \"escaping\" <characters>\'), unescaped: this.unescapeHtml(\'test &quot;unescaping&quot; &lt;characters&gt;\')}); };'
@agent.save!
expect {
expect {
@agent.check
}.not_to change { AgentLog.count }
}.to change { Event.count}.by(1)
}.not_to(change { AgentLog.count })
}.to change { Event.count }.by(1)
created_event = @agent.events.last
expect(created_event.payload).to eq({ 'escaped' => 'test &quot;escaping&quot; &lt;characters&gt;', 'unescaped' => 'test "unescaping" <characters>'})
expect(created_event.payload).to eq({ 'escaped' => 'test &quot;escaping&quot; &lt;characters&gt;',
'unescaped' => 'test "unescaping" <characters>' })
end
end

describe "getting incoming events" do
it "can access incoming events in the JavaScript enviroment via this.incomingEvents" do
event = Event.new
event.agent = agents(:bob_rain_notifier_agent)
event.payload = { :data => "Something you should know about" }
event.payload = { data: "Something you should know about" }
event.save!
event.reload

Expand All @@ -331,10 +335,11 @@
expect {
expect {
@agent.receive([events(:bob_website_agent_event), event])
}.not_to change { AgentLog.count }
}.not_to(change { AgentLog.count })
}.to change { Event.count }.by(2)
created_event = @agent.events.first
expect(created_event.payload).to eq({ 'message' => "I got an event!", 'event_was' => { 'data' => "Something you should know about" } })
expect(created_event.payload).to eq({ 'message' => "I got an event!",
'event_was' => { 'data' => "Something you should know about" } })
end
end

Expand All @@ -353,7 +358,6 @@

expect {
expect {

@agent.check
expect(@agent.memory['callCount']).not_to be_present

Expand All @@ -367,9 +371,8 @@
@agent.memory['callCount'] = 20
@agent.check
expect(@agent.memory['callCount']).to eq(21)

}.not_to change { AgentLog.count }
}.not_to change { Event.count }
}.not_to(change { AgentLog.count })
}.not_to(change { Event.count })
end
end

Expand Down Expand Up @@ -413,4 +416,52 @@
end
end
end

describe "KVS" do
before do
Agents::KeyValueStoreAgent.create!(
name: "kvs1",
options: {
key: "{{ key }}",
value: "{{ value }}",
variable: "var1",
},
memory: {
x: "Huginn",
},
user: users(:jane),
control_targets: [@agent]
)

Agents::KeyValueStoreAgent.create!(
name: "kvs2",
options: {
key: "{{ key }}",
value: "{{ value }}",
variable: "var2",
},
memory: {
y: "Muninn",
},
user: users(:jane),
control_targets: [@agent]
)
end

it "can be accessed via Agent.kvs()" do
@agent.options['code'] = <<~JS
Agent.check = function() {
this.createEvent({ message: `I got values from KVS: ${this.kvs.var1["x"]} and ${this.kvs.var2["y"]}.` });
};
JS
@agent.save!

expect {
@agent.check
}.to change { @agent.events.count }.by(1)

created_event = @agent.events.last
expect(created_event.payload).to eq("message" => "I got values from KVS: Huginn and Muninn.")
end
end
end

0 comments on commit bb29e25

Please sign in to comment.