@@ -60,6 +60,7 @@ class ScenarioImport  | 
            ||
| 60 | 60 | 
                description = parsed_data['description']  | 
            
| 61 | 61 | 
                name = parsed_data['name']  | 
            
| 62 | 62 | 
                links = parsed_data['links']  | 
            
| 63 | 
                + control_links = parsed_data['control_links'] || []  | 
            |
| 63 | 64 | 
                tag_fg_color = parsed_data['tag_fg_color']  | 
            
| 64 | 65 | 
                tag_bg_color = parsed_data['tag_bg_color']  | 
            
| 65 | 66 | 
                source_url = parsed_data['source_url'].presence || nil  | 
            
                @@ -87,12 +88,19 @@ class ScenarioImport  | 
            ||
| 87 | 88 | 
                end  | 
            
| 88 | 89 | 
                agent  | 
            
| 89 | 90 | 
                end  | 
            
| 91 | 
                +  | 
            |
| 90 | 92 | 
                if success  | 
            
| 91 | 93 | 
                links.each do |link|  | 
            
| 92 | 94 | 
                receiver = created_agents[link['receiver']]  | 
            
| 93 | 95 | 
                source = created_agents[link['source']]  | 
            
| 94 | 96 | 
                receiver.sources << source unless receiver.sources.include?(source)  | 
            
| 95 | 97 | 
                end  | 
            
| 98 | 
                +  | 
            |
| 99 | 
                + control_links.each do |control_link|  | 
            |
| 100 | 
                + controller = created_agents[control_link['controller']]  | 
            |
| 101 | 
                + control_target = created_agents[control_link['control_target']]  | 
            |
| 102 | 
                + controller.control_targets << control_target unless controller.control_targets.include?(control_target)  | 
            |
| 103 | 
                + end  | 
            |
| 96 | 104 | 
                end  | 
            
| 97 | 105 | 
                end  | 
            
| 98 | 106 | 
                 | 
            
                @@ -20,7 +20,8 @@ class AgentsExporter  | 
            ||
| 20 | 20 | 
                :tag_bg_color => options[:tag_bg_color],  | 
            
| 21 | 21 | 
                :exported_at => Time.now.utc.iso8601,  | 
            
| 22 | 22 | 
                       :agents => agents.map { |agent| agent_as_json(agent) },
               | 
            
| 23 | 
                - :links => links  | 
            |
| 23 | 
                + :links => links,  | 
            |
| 24 | 
                + :control_links => control_links  | 
            |
| 24 | 25 | 
                }  | 
            
| 25 | 26 | 
                end  | 
            
| 26 | 27 | 
                 | 
            
                @@ -32,14 +33,26 @@ class AgentsExporter  | 
            ||
| 32 | 33 | 
                agent_ids = agents.map(&:id)  | 
            
| 33 | 34 | 
                 | 
            
| 34 | 35 | 
                contained_links = agents.map.with_index do |agent, index|  | 
            
| 35 | 
                - agent.links_as_source.where(:receiver_id => agent_ids).map do |link|  | 
            |
| 36 | 
                -        { :source => index, :receiver => agent_ids.index(link.receiver_id) }
               | 
            |
| 36 | 
                + agent.links_as_source.where(receiver_id: agent_ids).map do |link|  | 
            |
| 37 | 
                +        { source: index, receiver: agent_ids.index(link.receiver_id) }
               | 
            |
| 37 | 38 | 
                end  | 
            
| 38 | 39 | 
                end  | 
            
| 39 | 40 | 
                 | 
            
| 40 | 41 | 
                contained_links.flatten.compact  | 
            
| 41 | 42 | 
                end  | 
            
| 42 | 43 | 
                 | 
            
| 44 | 
                + def control_links  | 
            |
| 45 | 
                + agent_ids = agents.map(&:id)  | 
            |
| 46 | 
                +  | 
            |
| 47 | 
                + contained_controller_links = agents.map.with_index do |agent, index|  | 
            |
| 48 | 
                + agent.control_links_as_controller.where(control_target_id: agent_ids).map do |control_link|  | 
            |
| 49 | 
                +        { controller: index, control_target: agent_ids.index(control_link.control_target_id) }
               | 
            |
| 50 | 
                + end  | 
            |
| 51 | 
                + end  | 
            |
| 52 | 
                +  | 
            |
| 53 | 
                + contained_controller_links.flatten.compact  | 
            |
| 54 | 
                + end  | 
            |
| 55 | 
                +  | 
            |
| 43 | 56 | 
                def agent_as_json(agent)  | 
            
| 44 | 57 | 
                     {
               | 
            
| 45 | 58 | 
                :type => agent.type,  | 
            
                @@ -25,6 +25,7 @@ describe AgentsExporter do  | 
            ||
| 25 | 25 | 
                expect(data[:tag_bg_color]).to eq(tag_bg_color)  | 
            
| 26 | 26 | 
                expect(Time.parse(data[:exported_at])).to be_within(2).of(Time.now.utc)  | 
            
| 27 | 27 | 
                       expect(data[:links]).to eq([{ :source => 0, :receiver => 1 }])
               | 
            
| 28 | 
                + expect(data[:control_links]).to eq([])  | 
            |
| 28 | 29 | 
                       expect(data[:agents]).to eq(agent_list.map { |agent| exporter.agent_as_json(agent) })
               | 
            
| 29 | 30 | 
                       expect(data[:agents].all? { |agent_json| agent_json[:guid].present? && agent_json[:type].present? && agent_json[:name].present? }).to be_truthy
               | 
            
| 30 | 31 | 
                 | 
            
                @@ -38,6 +39,13 @@ describe AgentsExporter do  | 
            ||
| 38 | 39 | 
                 | 
            
| 39 | 40 | 
                       expect(exporter.as_json[:links]).to eq([{ :source => 0, :receiver => 1 }])
               | 
            
| 40 | 41 | 
                end  | 
            
| 42 | 
                +  | 
            |
| 43 | 
                + it "outputs control links to agents within the incoming set, but not outside it" do  | 
            |
| 44 | 
                + agents(:jane_rain_notifier_agent).control_targets = [agents(:jane_weather_agent), agents(:jane_basecamp_agent)]  | 
            |
| 45 | 
                + agents(:jane_rain_notifier_agent).save!  | 
            |
| 46 | 
                +  | 
            |
| 47 | 
                +      expect(exporter.as_json[:control_links]).to eq([{ :controller => 1, :control_target => 0 }])
               | 
            |
| 48 | 
                + end  | 
            |
| 41 | 49 | 
                end  | 
            
| 42 | 50 | 
                 | 
            
| 43 | 51 | 
                describe "#filename" do  | 
            
                @@ -74,7 +74,8 @@ describe ScenarioImport do  | 
            ||
| 74 | 74 | 
                ],  | 
            
| 75 | 75 | 
                :links => [  | 
            
| 76 | 76 | 
                         { :source => 0, :receiver => 1 }
               | 
            
| 77 | 
                - ]  | 
            |
| 77 | 
                + ],  | 
            |
| 78 | 
                + :control_links => []  | 
            |
| 78 | 79 | 
                }  | 
            
| 79 | 80 | 
                end  | 
            
| 80 | 81 | 
                   let(:valid_data) { valid_parsed_data.to_json }
               | 
            
                @@ -226,6 +227,38 @@ describe ScenarioImport do  | 
            ||
| 226 | 227 | 
                scenario_import.import  | 
            
| 227 | 228 | 
                           }.to change { users(:bob).agents.count }.by(2)
               | 
            
| 228 | 229 | 
                end  | 
            
| 230 | 
                +  | 
            |
| 231 | 
                + describe "with control links" do  | 
            |
| 232 | 
                + it 'creates the links' do  | 
            |
| 233 | 
                + valid_parsed_data[:control_links] = [  | 
            |
| 234 | 
                +              { :controller => 1, :control_target => 0 }
               | 
            |
| 235 | 
                + ]  | 
            |
| 236 | 
                +  | 
            |
| 237 | 
                +            expect {
               | 
            |
| 238 | 
                + scenario_import.import  | 
            |
| 239 | 
                +            }.to change { users(:bob).agents.count }.by(2)
               | 
            |
| 240 | 
                +  | 
            |
| 241 | 
                + weather_agent = scenario_import.scenario.agents.find_by(:guid => "a-weather-agent")  | 
            |
| 242 | 
                + trigger_agent = scenario_import.scenario.agents.find_by(:guid => "a-trigger-agent")  | 
            |
| 243 | 
                +  | 
            |
| 244 | 
                + expect(trigger_agent.sources).to eq([weather_agent])  | 
            |
| 245 | 
                + expect(weather_agent.controllers.to_a).to eq([trigger_agent])  | 
            |
| 246 | 
                + expect(trigger_agent.control_targets.to_a).to eq([weather_agent])  | 
            |
| 247 | 
                + end  | 
            |
| 248 | 
                +  | 
            |
| 249 | 
                + it "doesn't crash without any control links" do  | 
            |
| 250 | 
                + valid_parsed_data.delete(:control_links)  | 
            |
| 251 | 
                +  | 
            |
| 252 | 
                +            expect {
               | 
            |
| 253 | 
                + scenario_import.import  | 
            |
| 254 | 
                +            }.to change { users(:bob).agents.count }.by(2)
               | 
            |
| 255 | 
                +  | 
            |
| 256 | 
                + weather_agent = scenario_import.scenario.agents.find_by(:guid => "a-weather-agent")  | 
            |
| 257 | 
                + trigger_agent = scenario_import.scenario.agents.find_by(:guid => "a-trigger-agent")  | 
            |
| 258 | 
                +  | 
            |
| 259 | 
                + expect(trigger_agent.sources).to eq([weather_agent])  | 
            |
| 260 | 
                + end  | 
            |
| 261 | 
                + end  | 
            |
| 229 | 262 | 
                end  | 
            
| 230 | 263 | 
                 | 
            
| 231 | 264 | 
                describe "#generate_diff" do  |