diff --git a/app/models/simulator.rb b/app/models/simulator.rb index 102c712c..3b9a6dd0 100644 --- a/app/models/simulator.rb +++ b/app/models/simulator.rb @@ -3,22 +3,18 @@ class Simulator include Mongoid::Timestamps include Executable - WEBHOOK_CONDITION = {0=>:all_finished, 1=>:each_ps_finished} - field :name, type: String field :description, type: String field :sequential_seed, type: Boolean, default: false field :position, type: Integer # position in the table. start from zero field :to_be_destroyed, type: Boolean, default: false - field :webhook_url, type: String, default: "" - field :webhook_condition, type: Symbol , default: WEBHOOK_CONDITION[1] - field :webhook_triggered, type: Hash, default: {} # save conditios: {ps_id => {created: 0, submitted: 0, running: 0, finished: 0, failed: 0}} embeds_many :parameter_definitions has_many :parameter_sets, dependent: :destroy has_many :runs has_many :parameter_set_filters, dependent: :destroy has_many :analyzers, dependent: :destroy, autosave: true #enable autosave to copy analyzers has_many :save_tasks, dependent: :destroy + has_one :webhook, dependent: :destroy default_scope ->{ where(to_be_destroyed: false) } @@ -29,6 +25,7 @@ class Simulator before_create :set_position after_create :create_simulator_dir + after_create :create_a_webhook before_destroy :delete_simulator_dir public @@ -391,76 +388,9 @@ def runs_csv(runs = self.runs) end end - def http_post(url, data) - req = Net::HTTP::Post.new(url.path) - req.set_form_data(data) - res = http.request(req) - return res - end - - def webhook - trigger_condition = {} - ParameterSet.runs_status_count_batch(self.parameter_sets).each do |key, val| - trigger_condition[key.to_s] = val - end - return if trigger_condition == self.webhook_triggered - return if self.webhook_url.length == 0 - - ps_ids = trigger_condition.keys - ps_status = ps_ids.map do |ps_id| - [:created, :submitted, :running].map do |sym| - trigger_condition[ps_id][sym] - end.inject(:+) - end - - # when the condition is all_finished - if self.webhook_condition == WEBHOOK_CONDITION[0] and ps_status.inject(:+) == 0 - url = "/" + self.id.to_s - sim_name = self.name - payload={ - "username": "oacis bot", - "icon_url": "https://slack.com/img/icons/app-57.png" - } - payload["text"] = <<~EOS - This is posted by #oacis. - EOS - payload["text"] += <<~EOS - Info: All run on Simulator("#{self.id.to_s}") was finished. - EOS - res = http_post(self.webhook_url, "payload=#{payload.to_json}") - end - - # when the condition is each_ps_finished - if self.webhook_condition == WEBHOOK_CONDITION[1] - triggered_ps_ids = ps_ids.map.with_index do |ps_id, i| - id = ps_id - if self.webhook_triggered[ps_id] - old_status = [:created, :submitted, :running].map do |sym| self.webhook_triggered[ps_id][sym] end.inject(:+) - id = nil unless ps_status[i] == 0 and old_status > 0 - else - id = nil unless ps_status[i] == 0 - end - id - end.compact - payload={ - "username": "oacis bot", - "icon_url": "https://slack.com/img/icons/app-57.png" - } - payload["text"] = <<~EOS - This is posted by #oacis. - EOS - triggered_ps_ids.each do |ps_id| - url = "/" + self.id.to_s + "/" + ps_id - payload["text"] += <<~EOS - Info: All run on ParameterSet("#{ps_id}") was finished. - EOS - end - if triggered_ps_ids.size > 0 - res = http_post(self.webhook_url, "payload=#{payload.to_json}") - end - end - - # save the trigger_condition - self.timeless.update_attribute(:webhook_triggered, trigger_condition) + private + def create_a_webhook + wh = self.build_webhook + wh.save end end diff --git a/app/models/webhook.rb b/app/models/webhook.rb new file mode 100644 index 00000000..7616c7ee --- /dev/null +++ b/app/models/webhook.rb @@ -0,0 +1,87 @@ +class Webhook + include Mongoid::Document + + WEBHOOK_CONDITION = {0=>:all_finished, 1=>:each_ps_finished} + + field :webhook_url, type: String, default: "" + field :webhook_condition, type: Symbol , default: WEBHOOK_CONDITION[1] + field :webhook_triggered, type: Hash, default: {} # save conditios: {ps_id => {created: 0, submitted: 0, running: 0, finished: 0, failed: 0}} + + belongs_to :simulator + + private + def http_post(url, data) + uri = URI.parse(url) + http = Net::HTTP.new(uri.host, uri.port) + req = Net::HTTP::Post.new(url) + req.set_form_data(data) + res = http.request(req) + return res + end + + private + def run + trigger_condition = {} + ParameterSet.runs_status_count_batch(simulator.parameter_sets).each do |key, val| + trigger_condition[key.to_s] = val + end + return if trigger_condition == self.webhook_triggered + return if self.webhook_url.length == 0 + + ps_ids = trigger_condition.keys + ps_status = ps_ids.map do |ps_id| + [:created, :submitted, :running].map do |sym| + trigger_condition[ps_id][sym] + end.inject(:+) + end + # when the condition is all_finished + if self.webhook_condition == WEBHOOK_CONDITION[0] and ps_status.inject(:+) == 0 + url = "/" + simulator.id.to_s + sim_name = simulator.name + payload={ + "username": "oacis bot", + "icon_url": "https://slack.com/img/icons/app-57.png" + } + payload["text"] = <<~EOS + This is posted by #oacis. + EOS + payload["text"] += <<~EOS + Info: All run on Simulator("#{simulator.id.to_s}") was finished. + EOS + res = http_post(self.webhook_url, {"payload"=>payload}) + end + + # when the condition is each_ps_finished + if self.webhook_condition == WEBHOOK_CONDITION[1] + triggered_ps_ids = ps_ids.map.with_index do |ps_id, i| + id = ps_id + if self.webhook_triggered[ps_id] + old_status = [:created, :submitted, :running].map do |sym| self.webhook_triggered[ps_id][sym] end.inject(:+) + id = nil unless ps_status[i] == 0 and old_status > 0 + else + id = nil unless ps_status[i] == 0 + end + id + end.compact + payload={ + "username": "oacis bot", + "icon_url": "https://slack.com/img/icons/app-57.png" + } + payload["text"] = <<~EOS + This is posted by #oacis. + EOS + triggered_ps_ids.each do |ps_id| + url = "/" + simulator.id.to_s + "/" + ps_id + payload["text"] += <<~EOS + Info: All run on ParameterSet("#{ps_id}") was finished. + EOS + end + if triggered_ps_ids.size > 0 + res = http_post(self.webhook_url, {"payload"=>payload}) + end + end + + # save the trigger_condition + self.update_attribute(:webhook_triggered, trigger_condition) + end +end diff --git a/spec/models/simulator_spec.rb b/spec/models/simulator_spec.rb index 5ea928f0..e188d057 100644 --- a/spec/models/simulator_spec.rb +++ b/spec/models/simulator_spec.rb @@ -778,60 +778,4 @@ end end end - - describe "webhook can be triggerd" do - - before(:each) do - @sim = FactoryBot.create(:simulator, parameter_sets_count: 2, runs_count: 0) - @sim.webhook_url= "https://example.com" - @sim.save! - allow(@sim).to receive(:http_post).with(anything(), anything()).and_return("Success") - end - - it "with condition: all_finished" do - - @sim.webhook_condition = Simulator::WEBHOOK_CONDITION[0] # all_finished - @sim.save! - @sim.webhook #do not call http_post - @sim.parameter_sets.each do |ps| - run = ps.runs.build - run.status = :finished - run.save - end - trigger_condition = {} - ParameterSet.runs_status_count_batch(@sim.parameter_sets).map do |key, val| - trigger_condition[key.to_s] = val.map{|k,v| [k.to_s, v] }.to_h - end - # if webhook condition is satisfied - expect(@sim).to have_received(:http_post).once - @sim.webhook # call http_post - @sim.reload - # webhook_condition and webhook_triggerd are updated - expect(@sim.webhook_condition).to eq(Simulator::WEBHOOK_CONDITION[0]) - expect(@sim.webhook_triggered).to eq(trigger_condition) - end - - it "with condition: each_ps_finished" do - - @sim.webhook_condition = Simulator::WEBHOOK_CONDITION[1] # each_ps_finished - @sim.save! - @sim.webhook #do not call http_post - @sim.parameter_sets.each do |ps| - run = ps.runs.build - run.status = :finished - run.save - end - trigger_condition = {} - ParameterSet.runs_status_count_batch(@sim.parameter_sets).map do |key, val| - trigger_condition[key.to_s] = val.map{|k,v| [k.to_s, v] }.to_h - end - # if webhook condition is satisfied - expect(@sim).to have_received(:http_post).once - @sim.webhook # call http_post - @sim.reload - # webhook_condition and webhook_triggerd are updated - expect(@sim.webhook_condition).to eq(Simulator::WEBHOOK_CONDITION[1]) - expect(@sim.webhook_triggered).to eq(trigger_condition) - end - end end diff --git a/spec/models/webhook_spec.rb b/spec/models/webhook_spec.rb new file mode 100644 index 00000000..f68c7994 --- /dev/null +++ b/spec/models/webhook_spec.rb @@ -0,0 +1,63 @@ +require 'spec_helper' + +describe Webhook do + + describe "webhook can be triggerd" do + + before(:each) do + @sim = FactoryBot.create(:simulator, parameter_sets_count: 2, runs_count: 0) + @webhook = @sim.webhook + @webhook.webhook_url = "https://example.com" + @webhook.save! + http_mock = instance_double(Net::HTTP) + allow(Net::HTTP).to receive(:new).with(anything(), anything()).and_return(http_mock) + allow(http_mock).to receive(:request).with(anything()).and_return("Success") + end + + it "with condition: all_finished" do + + @webhook.webhook_condition = Webhook::WEBHOOK_CONDITION[0] # all_finished + @webhook.save! + @webhook.run #do not call http_post + @sim.parameter_sets.each do |ps| + run = ps.runs.build + run.status = :finished + run.save + end + trigger_condition = {} + ParameterSet.runs_status_count_batch(@sim.parameter_sets).map do |key, val| + trigger_condition[key.to_s] = val.map{|k,v| [k.to_s, v] }.to_h + end + # if webhook condition is satisfied + expect(Net::HTTP).to have_received(:new).once + @webhook.run # call http_post + @webhook.reload + # webhook_condition and webhook_triggerd are updated + expect(@webhook.webhook_condition).to eq(Webhook::WEBHOOK_CONDITION[0]) + expect(@webhook.webhook_triggered).to eq(trigger_condition) + end + + it "with condition: each_ps_finished" do + + @webhook.webhook_condition = Webhook::WEBHOOK_CONDITION[1] # each_ps_finished + @webhook.save! + @webhook.run #do not call http_post + @sim.parameter_sets.each do |ps| + run = ps.runs.build + run.status = :finished + run.save + end + trigger_condition = {} + ParameterSet.runs_status_count_batch(@sim.parameter_sets).map do |key, val| + trigger_condition[key.to_s] = val.map{|k,v| [k.to_s, v] }.to_h + end + # if webhook condition is satisfied + expect(Net::HTTP).to have_received(:new).once + @webhook.run # call http_post + @webhook.reload + # webhook_condition and webhook_triggerd are updated + expect(@webhook.webhook_condition).to eq(Webhook::WEBHOOK_CONDITION[1]) + expect(@webhook.webhook_triggered).to eq(trigger_condition) + end + end +end \ No newline at end of file