Merge pull request #3 from cantino/dsander-omniauth

Moving ENV variables
This commit is contained in:
Dominik Sander 2014-08-07 17:38:01 +02:00
commit 8cf4b3d3bf
37 changed files with 277 additions and 110 deletions

View file

@ -59,6 +59,7 @@ gem 'faraday', '~> 0.9.0'
gem 'faraday_middleware'
gem 'typhoeus', '~> 0.6.3'
gem 'nokogiri', '~> 1.6.1'
gem 'net-ftp-list', '~> 3.2.8'
gem 'wunderground', '~> 1.2.0'
gem 'forecast_io', '~> 2.0.0'

View file

@ -188,6 +188,7 @@ GEM
multipart-post (2.0.0)
mysql2 (0.3.16)
naught (1.0.0)
net-ftp-list (3.2.8)
nokogiri (1.6.3.1)
mini_portile (= 0.6.0)
oauth (0.4.7)
@ -418,6 +419,7 @@ DEPENDENCIES
liquid (~> 2.6.1)
mqtt
mysql2 (~> 0.3.16)
net-ftp-list (~> 3.2.8)
nokogiri (~> 1.6.1)
omniauth
omniauth-37signals

View file

@ -27,6 +27,8 @@ Follow [@tectonic](https://twitter.com/tectonic) for updates as Huginn evolves,
Want to help with Huginn? All contributions are encouraged! You could make UI improvements, add new Agents, write documentation and tutorials, or try tackling [issues tagged with #help-wanted](https://github.com/cantino/huginn/issues?direction=desc&labels=help-wanted&page=1&sort=created&state=open).
Really want an issue fixed/feature implemented? Or maybe you just want to solve some community issues and earn some extra coffee money? Then you should take a look at the [current bounties on Bountysource](https://www.bountysource.com/trackers/282580-huginn).
Have an awesome an idea but not feeling quite up to contributing yet? Head over to our [Official 'suggest an agent' thread ](https://github.com/cantino/huginn/issues/353) and tell us about your cool idea!
## Examples
@ -105,5 +107,5 @@ Huginn is a work in progress and is just getting started. Please get involved!
Please fork, add specs, and send pull requests!
[![Build Status](https://travis-ci.org/cantino/huginn.png)](https://travis-ci.org/cantino/huginn) [![Coverage Status](https://coveralls.io/repos/cantino/huginn/badge.png)](https://coveralls.io/r/cantino/huginn) [![Bitdeli Badge](https://d2weczhvl823v0.cloudfront.net/cantino/huginn/trend.png)](https://bitdeli.com/free "Bitdeli Badge") [![Dependency Status](https://gemnasium.com/cantino/huginn.svg)](https://gemnasium.com/cantino/huginn)
[![Build Status](https://travis-ci.org/cantino/huginn.png)](https://travis-ci.org/cantino/huginn) [![Coverage Status](https://coveralls.io/repos/cantino/huginn/badge.png)](https://coveralls.io/r/cantino/huginn) [![Bitdeli Badge](https://d2weczhvl823v0.cloudfront.net/cantino/huginn/trend.png)](https://bitdeli.com/free "Bitdeli Badge") [![Dependency Status](https://gemnasium.com/cantino/huginn.svg)](https://gemnasium.com/cantino/huginn) [![Bountysource](https://www.bountysource.com/badge/tracker?tracker_id=282580)](https://www.bountysource.com/trackers/282580-huginn?utm_source=282580&utm_medium=shield&utm_campaign=TRACKER_BADGE)

View file

@ -0,0 +1,20 @@
$ ->
svg = document.querySelector('.agent-diagram svg.diagram')
overlay = document.querySelector('.agent-diagram .overlay')
getTopLeft = (node) ->
bbox = node.getBBox()
point = svg.createSVGPoint()
point.x = bbox.x + bbox.width
point.y = bbox.y
point.matrixTransform(node.getCTM())
$(svg).find('g.node[data-badge-id]').each ->
tl = getTopLeft(this)
$('#' + this.getAttribute('data-badge-id'), overlay).each ->
badge = $(this)
badge.css
left: tl.x - badge.outerWidth() * (2/3)
top: tl.y - badge.outerHeight() * (1/3)
'background-color': badge.find('.label').css('background-color')
.show()
return
return

View file

@ -0,0 +1,30 @@
.agent-diagram {
position: relative;
z-index: auto;
svg.diagram {
position: absolute;
z-index: 1;
}
.overlay-container {
position: absolute;
top: 0;
left: 0;
z-index: auto;
.overlay {
position: relative;
z-index: auto;
width: 100%;
height: 100%;
.badge {
position: absolute;
display: none;
color: white !important;
z-index: 2;
}
}
}
}

View file

@ -98,14 +98,6 @@ class AgentsController < ApplicationController
@agent = current_user.agents.find(params[:id])
end
def diagram
@agents = if params[:scenario_id].present?
current_user.scenarios.find(params[:scenario_id]).agents.includes(:receivers)
else
current_user.agents.includes(:receivers)
end
end
def create
@agent = Agent.build_for_type(params[:agent].delete(:type),
current_user,

View file

@ -0,0 +1,9 @@
class DiagramsController < ApplicationController
def show
@agents = if params[:scenario_id].present?
current_user.scenarios.find(params[:scenario_id]).agents.includes(:receivers)
else
current_user.agents.includes(:receivers)
end
end
end

View file

@ -2,8 +2,8 @@ class EventsController < ApplicationController
before_filter :load_event, :except => :index
def index
if params[:agent]
@agent = current_user.agents.find(params[:agent])
if params[:agent_id]
@agent = current_user.agents.find(params[:agent_id])
@events = @agent.events.page(params[:page])
else
@events = current_user.events.preload(:agent).page(params[:page])

View file

@ -6,7 +6,7 @@ module DotHelper
dot.close_write
dot.read
} rescue false)
svg.html_safe
decorate_svg(svg, agents).html_safe
else
tag('img', src: URI('https://chart.googleapis.com/chart').tap { |uri|
uri.query = URI.encode_www_form(cht: 'gv', chl: agents_dot(agents))
@ -57,6 +57,13 @@ module DotHelper
end
end
def ids(values)
values.each_with_index { |id, i|
raw ' ' if i > 0
id id
}
end
def attr_list(attrs = nil)
return if attrs.nil?
attrs = attrs.select { |key, value| value.present? }
@ -86,16 +93,13 @@ module DotHelper
end
def statement(ids, attrs = nil)
Array(ids).each_with_index { |id, i|
raw ' ' if i > 0
id id
}
ids Array(ids)
attr_list attrs
raw ';'
end
def block(title, &block)
raw title
def block(*ids, &block)
ids ids
raw '{'
block.call
raw '}'
@ -112,11 +116,7 @@ module DotHelper
draw(agents: agents,
agent_id: ->agent { 'a%d' % agent.id },
agent_label: ->agent {
if agent.disabled?
'%s (Disabled)' % agent.name
else
agent.name
end.gsub(/(.{20}\S*)\s+/) {
agent.name.gsub(/(.{20}\S*)\s+/) {
# Fold after every 20+ characters
$1 + "\n"
}
@ -128,6 +128,7 @@ module DotHelper
def agent_node(agent)
node(agent_id[agent],
label: agent_label[agent],
tooltip: (agent.short_type.titleize if rich),
URL: (agent_url[agent] if rich),
style: ('rounded,dashed' if agent.disabled?),
color: (@disabled if agent.disabled?),
@ -141,7 +142,7 @@ module DotHelper
color: (@disabled if agent.disabled? || receiver.disabled?))
end
block('digraph foo') {
block('digraph', 'Agent Event Flow') {
# statement 'graph', rankdir: 'LR'
statement 'node',
shape: 'box',
@ -160,4 +161,60 @@ module DotHelper
}
}
end
def decorate_svg(xml, agents)
svg = Nokogiri::XML(xml).at('svg')
Nokogiri::HTML::Document.new.tap { |doc|
doc << root = Nokogiri::XML::Node.new('div', doc) { |div|
div['class'] = 'agent-diagram'
}
svg['class'] = 'diagram'
root << svg
root << overlay_container = Nokogiri::XML::Node.new('div', doc) { |div|
div['class'] = 'overlay-container'
div['style'] = "width: #{svg['width']}; height: #{svg['height']}"
}
overlay_container << overlay = Nokogiri::XML::Node.new('div', doc) { |div|
div['class'] = 'overlay'
}
svg.xpath('//xmlns:g[@class="node"]', svg.namespaces).each { |node|
agent_id = (node.xpath('./xmlns:title/text()', svg.namespaces).to_s[/\d+/] or next).to_i
agent = agents.find { |a| a.id == agent_id }
count = agent.events_count
next unless count && count > 0
overlay << Nokogiri::XML::Node.new('a', doc) { |badge|
badge['id'] = id = 'b%d' % agent_id
badge['class'] = 'badge'
badge['href'] = events_path(agent: agent)
badge['target'] = '_blank'
badge['title'] = "#{count} events created"
badge.content = count.to_s
node['data-badge-id'] = id
badge << Nokogiri::XML::Node.new('span', doc) { |label|
# a dummy label only to obtain the background color
label['class'] = [
'label',
if agent.disabled?
'label-warning'
elsif agent.working?
'label-success'
else
'label-danger'
end
].join(' ')
label['style'] = 'display: none';
}
}
}
# See also: app/assets/diagram.js.coffee
}.at('div.agent-diagram').to_s
end
end

View file

@ -25,9 +25,14 @@ module Agents
"instructions": {
"message": "Today's conditions look like {{conditions}} with a high temperature of {{high.celsius}} degrees Celsius.",
"subject": "{{data}}"
"subject": "{{data}}",
"created_at": "{{created_at}}"
}
Names here like `conditions`, `high` and `data` refer to the corresponding values in the Event hash.
The special key `created_at` refers to the timestamp of the Event, which can be reformatted by the `date` filter, like `{{created_at | date:"at %I:%M %p" }}`.
The upstream agent of each received event is accessible via the key `agent`, which has the following attributes: #{''.tap { |s| s << AgentDrop.instance_methods(false).map { |m| "`#{m}`" }.join(', ') }}.
Have a look at the [Wiki](https://github.com/cantino/huginn/wiki/Formatting-Events-using-Liquid) to learn more about liquid templating.
@ -68,8 +73,6 @@ module Agents
If you want to retain original contents of events and only add new keys, then set `mode` to `merge`, otherwise set it to `clean`.
By default, the output event will have a `created_at` field added as well, reflecting the original Event creation time. You can skip this output by setting `skip_created_at` to `true`.
To CGI escape output (for example when creating a link), use the Liquid `uri_escape` filter, like so:
{
@ -82,7 +85,7 @@ module Agents
after_save :clear_matchers
def validate_options
errors.add(:base, "instructions, mode, and skip_created_at all need to be present.") unless options['instructions'].present? && options['mode'].present? && options['skip_created_at'].present?
errors.add(:base, "instructions and mode need to be present.") unless options['instructions'].present? && options['mode'].present?
validate_matchers
end
@ -96,7 +99,6 @@ module Agents
},
'matchers' => [],
'mode' => "clean",
'skip_created_at' => "false"
}
end
@ -110,7 +112,6 @@ module Agents
opts = interpolated(event.to_liquid(payload))
formatted_event = opts['mode'].to_s == "merge" ? event.payload.dup : {}
formatted_event.merge! opts['instructions']
formatted_event['created_at'] = event.created_at unless opts['skip_created_at'].to_s == "true"
create_event :payload => formatted_event
end
end

View file

@ -1,4 +1,5 @@
require 'net/ftp'
require 'net/ftp/list'
require 'uri'
require 'time'
@ -105,34 +106,15 @@ module Agents
# commands during iteration.
list = ftp.list('-a')
month2year = {}
list.each do |line|
mon, day, smtn, rest = line.split(' ', 9)[5..-1]
# Remove symlink target part if any
filename = rest[/\A(.+?)(?:\s+->\s|\z)/, 1]
entry = Net::FTP::List.parse line
filename = entry.basename
mtime = Time.parse(entry.mtime.to_s).utc
patterns.any? { |pattern|
File.fnmatch?(pattern, filename)
} or next
case smtn
when /:/
if year = month2year[mon]
mtime = Time.parse("#{mon} #{day} #{year} #{smtn} GMT")
else
log "Getting mtime of #{filename}"
mtime = ftp.mtime(filename)
month2year[mon] = mtime.year
end
else
# Do not bother calling MDTM for old files. Losing the
# time part only makes a timestamp go backwards, meaning
# that it will trigger no new event.
mtime = Time.parse("#{mon} #{day} #{smtn} GMT")
end
after < mtime or next
yield filename, mtime
@ -193,7 +175,7 @@ module Agents
found_entries[filename]
}.each { |filename|
create_event :payload => {
'url' => (base_uri + filename).to_s,
'url' => "#{base_uri}#{filename}",
'filename' => filename,
'timestamp' => found_entries[filename],
}

View file

@ -31,7 +31,7 @@ module Agents
end
def validate_options
errors.add(:base, "you need to specify a hipchat auth_token") unless options['auth_token'].present?
errors.add(:base, "you need to specify a hipchat auth_token or provide a credential named hipchat_auth_token") unless options['auth_token'].present? || credential('hipchat_auth_token').present?
errors.add(:base, "you need to specify a room_name or a room_name_path") if options['room_name'].blank? && options['room_name_path'].blank?
end
@ -40,10 +40,10 @@ module Agents
end
def receive(incoming_events)
client = HipChat::Client.new(interpolated[:auth_token])
client = HipChat::Client.new(interpolated[:auth_token] || credential('hipchat_auth_token'))
incoming_events.each do |event|
mo = interpolated(event)
client[mo[:room_name]].send(mo[:username], mo[:message], :notify => mo[:notify].to_s == 'true' ? 1 : 0, :color => mo[:color])
client[mo[:room_name]].send(mo[:username], mo[:message], :notify => boolify(mo[:notify]) ? 1 : 0, :color => mo[:color])
end
end
end

View file

@ -17,7 +17,7 @@ module Agents
Simply choose a topic (think email subject line) to publish/listen to, and configure your service.
It's easy to setup your own [broker](http://jpmens.net/2013/09/01/installing-mosquitto-on-a-raspberry-pi/) or connect to a [cloud service](www.cloudmqtt.com)
It's easy to setup your own [broker](http://jpmens.net/2013/09/01/installing-mosquitto-on-a-raspberry-pi/) or connect to a [cloud service](http://www.cloudmqtt.com)
Hints:
Many services run mqtts (mqtt over SSL) often with a custom certificate.

View file

@ -69,7 +69,7 @@ module Agents
def receive(incoming_events)
incoming_events.each do |event|
outgoing = interpolated(event)['payload'].presence || {}
if interpolated['no_merge'].to_s == 'true'
if boolify(interpolated['no_merge'])
handle outgoing, event.payload
else
handle outgoing.merge(event.payload), event.payload

View file

@ -102,7 +102,7 @@ module Agents
end
def keep_event?
interpolated['keep_event'] == 'true'
boolify(interpolated['keep_event'])
end
end
end

View file

@ -44,13 +44,13 @@ module Agents
incoming_events.each do |event|
message = (event.payload['message'].presence || event.payload['text'].presence || event.payload['sms'].presence).to_s
if message.present?
if interpolated(event)['receive_call'].to_s == 'true'
if boolify(interpolated(event)['receive_call'])
secret = SecureRandom.hex 3
memory['pending_calls'][secret] = message
make_call secret
end
if interpolated(event)['receive_text'].to_s == 'true'
if boolify(interpolated(event)['receive_text'])
message = message.slice 0..160
send_message message
end

View file

@ -56,6 +56,8 @@ class EventDrop
case key
when 'agent'
@object.agent
when 'created_at'
@object.created_at
end
end
end

View file

@ -53,7 +53,7 @@
</td>
<td class='<%= "agent-disabled" if agent.disabled? %>'>
<% if agent.can_create_events? %>
<%= link_to(agent.events_count || 0, events_path(:agent => agent.to_param)) %>
<%= link_to(agent.events_count || 0, agent_events_path(agent)) %>
<% else %>
<span class='not-applicable'></span>
<% end %>

View file

@ -12,9 +12,8 @@
<div class="btn-group">
<%= link_to '<span class="glyphicon glyphicon-plus"></span> New Agent'.html_safe, new_agent_path, class: "btn btn-default" %>
<%= link_to '<span class="glyphicon glyphicon-refresh"></span> Run event propagation'.html_safe, propagate_agents_path, method: 'post', class: "btn btn-default" %>
<%= link_to '<span class="glyphicon glyphicon-random"></span> View diagram'.html_safe, diagram_agents_path, class: "btn btn-default" %>
<%= link_to '<span class="glyphicon glyphicon-random"></span> View diagram'.html_safe, diagram_path, class: "btn btn-default" %>
</div>
</div>
</div>
</div>

View file

@ -15,7 +15,7 @@
<li><a href="#logs" data-toggle="tab" data-agent-id="<%= @agent.id %>" class='<%= @agent.recent_error_logs? ? 'recent-errors' : '' %>'><span class='glyphicon glyphicon-list-alt'></span> Logs</a></li>
<% if @agent.can_create_events? && @agent.events.count > 0 %>
<li><%= link_to '<span class="glyphicon glyphicon-random"></span> Events'.html_safe, events_path(:agent => @agent.to_param) %></li>
<li><%= link_to '<span class="glyphicon glyphicon-random"></span> Events'.html_safe, agent_events_path(@agent) %></li>
<% else %>
<li class='disabled'><a><span class='glyphicon glyphicon-random'></span> Events</a></li>
<% end %>
@ -103,7 +103,7 @@
<% if @agent.can_create_events? %>
<p>
<b>Events created:</b>
<%= link_to @agent.events.count, events_path(:agent => @agent.to_param) %>
<%= link_to @agent.events.count, agent_events_path(@agent) %>
</p>
<% end %>

View file

@ -1,3 +1,7 @@
<% content_for :head do %>
<%= javascript_include_tag "diagram" %>
<% end %>
<div class='container'>
<div class='row'>
<div class='col-md-12'>

View file

@ -41,7 +41,7 @@
agentPaths["New Agent"] = <%= Utils.jsonify new_agent_path %>;
agentPaths["Account"] = <%= Utils.jsonify edit_user_registration_path %>;
agentPaths["Events Index"] = <%= Utils.jsonify events_path %>;
agentPaths["View Agent Diagram"] = <%= Utils.jsonify diagram_agents_path %>;
agentPaths["View Agent Diagram"] = <%= Utils.jsonify diagram_path %>;
agentPaths["Run Event Propagation"] = { url: <%= Utils.jsonify propagate_agents_path %>, method: 'POST' };

View file

@ -15,7 +15,7 @@
<div class="btn-group">
<%= link_to '<span class="glyphicon glyphicon-chevron-left"></span> Back'.html_safe, scenarios_path, class: "btn btn-default" %>
<%= link_to '<span class="glyphicon glyphicon-random"></span> View Diagram'.html_safe, diagram_agents_path(:scenario_id => @scenario.to_param), class: "btn btn-default" %>
<%= link_to '<span class="glyphicon glyphicon-random"></span> View Diagram'.html_safe, scenario_diagram_path(@scenario), class: "btn btn-default" %>
<%= link_to '<span class="glyphicon glyphicon-edit"></span> Edit'.html_safe, edit_scenario_path(@scenario), class: "btn btn-default" %>
<% if @scenario.source_url.present? %>
<%= link_to '<span class="glyphicon glyphicon-plus"></span> Update'.html_safe, new_scenario_imports_path(:url => @scenario.source_url), class: "btn btn-default" %>

View file

@ -61,7 +61,7 @@ Huginn::Application.configure do
end
# Precompile additional assets (application.js.coffee.erb, application.css, and all non-JS/CSS are already added)
config.assets.precompile += %w( graphing.js user_credentials.js )
config.assets.precompile += %w( diagram.js graphing.js user_credentials.js )
# Ignore bad email addresses and do not raise email delivery errors.
# Set this to true and configure the email server for immediate delivery to raise delivery errors.

View file

@ -11,7 +11,6 @@ Huginn::Application.routes.draw do
post :propagate
get :type_details
get :event_descriptions
get :diagram
end
resources :logs, :only => [:index] do
@ -19,8 +18,12 @@ Huginn::Application.routes.draw do
delete :clear
end
end
resources :events, :only => [:index]
end
resource :diagram, :only => [:show]
resources :events, :only => [:index, :show, :destroy] do
member do
post :reemit
@ -36,6 +39,8 @@ Huginn::Application.routes.draw do
get :share
get :export
end
resource :diagram, :only => [:show]
end
resources :user_credentials, :except => :show

View file

@ -0,0 +1,21 @@
class ConvertEfaSkipCreatedAt < ActiveRecord::Migration
def up
Agent.where(type: 'Agents::EventFormattingAgent').each do |agent|
agent.options_will_change!
unless agent.options.delete('skip_created_at').to_s == 'true'
agent.options['instructions'] = {
'created_at' => '{{created_at}}'
}.update(agent.options['instructions'] || {})
end
agent.save!
end
end
def down
Agent.where(type: 'Agents::EventFormattingAgent').each do |agent|
agent.options_will_change!
agent.options['skip_created_at'] = (agent.options['instructions'] || {})['created_at'] == '{{created_at}}'
agent.save!
end
end
end

View file

@ -64,7 +64,7 @@ unless user.agents.where(:name => "Rain Notifier").exists?
'value' => "rain|storm",
'path' => "conditions"
}],
'message' => "Just so you know, it looks like '<conditions>' tomorrow in <location>"
'message' => "Just so you know, it looks like '{{conditions}}' tomorrow in {{location}}"
}).save!
end

View file

@ -15,12 +15,12 @@ describe EventsController do
it "can filter by Agent" do
sign_in users(:bob)
get :index, :agent => agents(:bob_website_agent)
get :index, :agent_id => agents(:bob_website_agent)
assigns(:events).length.should == agents(:bob_website_agent).events.length
assigns(:events).all? {|i| i.agent.should == agents(:bob_website_agent) }.should be_true
lambda {
get :index, :agent => agents(:jane_website_agent)
get :index, :agent_id => agents(:jane_website_agent)
}.should raise_error(ActiveRecord::RecordNotFound)
end
end

5
spec/env.test Normal file
View file

@ -0,0 +1,5 @@
APP_SECRET_TOKEN=notarealappsecrettoken
TWITTER_OAUTH_KEY=twitteroauthkey
TWITTER_OAUTH_SECRET=twitteroauthsecret
THIRTY_SEVEN_SIGNALS_OAUTH_KEY=TESTKEY
THIRTY_SEVEN_SIGNALS_OAUTH_SECRET=TESTSECRET

View file

@ -72,7 +72,7 @@ jane_rain_notifier_agent:
:value => "rain",
:path => "conditions"
}],
:message => "Just so you know, it looks like '<conditions>' tomorrow in <location>"
:message => "Just so you know, it looks like '{{conditions}}' tomorrow in {{location}}"
}.to_json.inspect %>
bob_rain_notifier_agent:
@ -87,7 +87,7 @@ bob_rain_notifier_agent:
:value => "rain",
:path => "conditions"
}],
:message => "Just so you know, it looks like '<conditions>' tomorrow in <location>"
:message => "Just so you know, it looks like '{{conditions}}' tomorrow in {{location}}"
}.to_json.inspect %>
bob_twitter_user_agent:

View file

@ -56,13 +56,13 @@ describe DotHelper do
it "generates a DOT script" do
agents_dot(@agents).should =~ %r{
\A
digraph \s foo \{
digraph \x20 "Agent \x20 Event \x20 Flow" \{
node \[ [^\]]+ \];
(?<foo>\w+) \[label=foo\];
\k<foo> -> (?<bar1>\w+) \[style=dashed\];
\k<foo> -> (?<bar2>\w+) \[color="\#999999"\];
\k<bar1> \[label=bar1\];
\k<bar2> \[label="bar2 \s \(Disabled\)",style="rounded,dashed",color="\#999999",fontcolor="\#999999"\];
\k<bar2> \[label=bar2,style="rounded,dashed",color="\#999999",fontcolor="\#999999"\];
\k<bar2> -> (?<bar3>\w+) \[style=dashed,color="\#999999"\];
\k<bar3> \[label=bar3\];
\}
@ -73,15 +73,15 @@ describe DotHelper do
it "generates a richer DOT script" do
agents_dot(@agents, true).should =~ %r{
\A
digraph \s foo \{
digraph \x20 "Agent \x20 Event \x20 Flow" \{
node \[ [^\]]+ \];
(?<foo>\w+) \[label=foo,URL="#{Regexp.quote(agent_path(@foo))}"\];
(?<foo>\w+) \[label=foo,tooltip="Dot \x20 Foo",URL="#{Regexp.quote(agent_path(@foo))}"\];
\k<foo> -> (?<bar1>\w+) \[style=dashed\];
\k<foo> -> (?<bar2>\w+) \[color="\#999999"\];
\k<bar1> \[label=bar1,URL="#{Regexp.quote(agent_path(@bar1))}"\];
\k<bar2> \[label="bar2 \s \(Disabled\)",URL="#{Regexp.quote(agent_path(@bar2))}",style="rounded,dashed",color="\#999999",fontcolor="\#999999"\];
\k<bar1> \[label=bar1,tooltip="Dot \x20 Bar",URL="#{Regexp.quote(agent_path(@bar1))}"\];
\k<bar2> \[label=bar2,tooltip="Dot \x20 Bar",URL="#{Regexp.quote(agent_path(@bar2))}",style="rounded,dashed",color="\#999999",fontcolor="\#999999"\];
\k<bar2> -> (?<bar3>\w+) \[style=dashed,color="\#999999"\];
\k<bar3> \[label=bar3,URL="#{Regexp.quote(agent_path(@bar3))}"\];
\k<bar3> \[label=bar3,tooltip="Dot \x20 Bar",URL="#{Regexp.quote(agent_path(@bar3))}"\];
\}
\z
}x

View file

@ -9,6 +9,8 @@ describe Agents::EventFormattingAgent do
:message => "Received {{content.text}} from {{content.name}} .",
:subject => "Weather looks like {{conditions}} according to the forecast at {{pretty_date.time}}",
:agent => "{{agent.type}}",
:created_at => "{{created_at}}",
:created_at_iso => "{{created_at | date:'%FT%T%:z'}}",
},
:mode => "clean",
:matchers => [
@ -18,7 +20,6 @@ describe Agents::EventFormattingAgent do
:to => "pretty_date",
},
],
:skip_created_at => "false"
}
}
@checker = Agents::EventFormattingAgent.new(@valid_params)
@ -53,18 +54,12 @@ describe Agents::EventFormattingAgent do
Event.last.payload[:content].should_not == nil
end
it "should accept skip_created_at" do
@checker.receive([@event])
Event.last.payload[:created_at].should_not == nil
@checker.options[:skip_created_at] = "true"
@checker.receive([@event])
Event.last.payload[:created_at].should == nil
end
it "should handle Liquid templating in instructions" do
@checker.receive([@event])
Event.last.payload[:message].should == "Received Some Lorem Ipsum from somevalue ."
Event.last.payload[:agent].should == "WeatherAgent"
Event.last.payload[:created_at].should == @event.created_at.to_s
Event.last.payload[:created_at_iso].should == @event.created_at.iso8601
end
it "should handle matchers and Liquid templating in instructions" do
@ -144,10 +139,5 @@ describe Agents::EventFormattingAgent do
@checker.options[:mode] = ""
@checker.should_not be_valid
end
it "should validate presence of skip_created_at" do
@checker.options[:skip_created_at] = ""
@checker.should_not be_valid
end
end
end

View file

@ -7,19 +7,23 @@ describe Agents::FtpsiteAgent do
@site = {
'expected_update_period_in_days' => 1,
'url' => "ftp://ftp.example.org/pub/releases/",
'patterns' => ["example-*.tar.gz"],
'patterns' => ["example*.tar.gz"],
}
@checker = Agents::FtpsiteAgent.new(:name => "Example", :options => @site, :keep_events_for => 2)
@checker.user = users(:bob)
@checker.save!
stub(@checker).each_entry.returns { |block|
block.call("example-latest.tar.gz", Time.parse("2014-04-01T10:00:01Z"))
block.call("example-1.0.tar.gz", Time.parse("2013-10-01T10:00:00Z"))
block.call("example-1.1.tar.gz", Time.parse("2014-04-01T10:00:00Z"))
}
end
describe "#check" do
before do
stub(@checker).each_entry.returns { |block|
block.call("example latest.tar.gz", Time.parse("2014-04-01T10:00:01Z"))
block.call("example-1.0.tar.gz", Time.parse("2013-10-01T10:00:00Z"))
block.call("example-1.1.tar.gz", Time.parse("2014-04-01T10:00:00Z"))
}
end
it "should validate the integer fields" do
@checker.options['expected_update_period_in_days'] = "nonsense"
lambda { @checker.save! }.should raise_error;
@ -33,7 +37,7 @@ describe Agents::FtpsiteAgent do
known_entries.sort_by(&:last).should == [
["example-1.0.tar.gz", "2013-10-01T10:00:00Z"],
["example-1.1.tar.gz", "2014-04-01T10:00:00Z"],
["example-latest.tar.gz", "2014-04-01T10:00:01Z"],
["example latest.tar.gz", "2014-04-01T10:00:01Z"],
]
}
@ -46,7 +50,7 @@ describe Agents::FtpsiteAgent do
lambda { @checker.check }.should_not change { Event.count }
stub(@checker).each_entry.returns { |block|
block.call("example-latest.tar.gz", Time.parse("2014-04-02T10:00:01Z"))
block.call("example latest.tar.gz", Time.parse("2014-04-02T10:00:01Z"))
# In the long list format the timestamp may look going
# backwards after six months: Oct 01 10:00 -> Oct 01 2013
@ -62,7 +66,7 @@ describe Agents::FtpsiteAgent do
["example-1.0.tar.gz", "2013-10-01T00:00:00Z"],
["example-1.1.tar.gz", "2014-04-01T10:00:00Z"],
["example-1.2.tar.gz", "2014-04-02T10:00:00Z"],
["example-latest.tar.gz", "2014-04-02T10:00:01Z"],
["example latest.tar.gz", "2014-04-02T10:00:01Z"],
]
}
@ -75,5 +79,33 @@ describe Agents::FtpsiteAgent do
lambda { @checker.check }.should_not change { Event.count }
end
end
describe "#each_entry" do
before do
stub.any_instance_of(Net::FTP).list.returns [ # Windows format
"04-02-14 10:01AM 288720748 example latest.tar.gz",
"04-01-14 10:05AM 288720710 no-match-example.tar.gz"
]
stub(@checker).open_ftp.yields Net::FTP.new
end
it "filters out files that don't match the given format" do
entries = []
@checker.each_entry { |a, b| entries.push [a, b] }
entries.size.should == 1
filename, mtime = entries.first
filename.should == 'example latest.tar.gz'
mtime.should == '2014-04-02T10:01:00Z'
end
it "filters out files that are older than the given date" do
@checker.options['after'] = '2015-10-21'
entries = []
@checker.each_entry { |a, b| entries.push [a, b] }
entries.size.should == 0
end
end
end
end

View file

@ -42,6 +42,12 @@ describe Agents::HipchatAgent do
@checker.should be_valid
end
it "should also allow a credential" do
@checker.options['auth_token'] = nil
@checker.should_not be_valid
@checker.user.user_credentials.create :credential_name => 'hipchat_auth_token', :credential_value => 'something'
@checker.reload.should be_valid
end
end
describe "#receive" do

View file

@ -85,6 +85,7 @@ describe EventDrop do
before do
@event = Event.new
@event.agent = agents(:jane_weather_agent)
@event.created_at = Time.at(1400000000)
@event.payload = {
'title' => 'some title',
'url' => 'http://some.site.example.org/',
@ -111,4 +112,9 @@ describe EventDrop do
t = '{{agent.name}}'
interpolate(t, @event).should eq('SF Weather')
end
it 'should have created_at' do
t = '{{created_at | date:"%FT%T%z" }}'
interpolate(t, @event).should eq('2014-05-13T09:53:20-0700')
end
end

View file

@ -59,8 +59,6 @@ describe Service do
stub_request(:post, "https://launchpad.37signals.com/authorization/token?client_id=TESTKEY&client_secret=TESTSECRET&refresh_token=refreshtokentest&type=refresh").
to_return(:status => 200, :body => '{"expires_in":1209600,"access_token": "NEWTOKEN"}', :headers => {})
@service.provider = '37signals'
ENV['THIRTY_SEVEN_SIGNALS_OAUTH_KEY'] = 'TESTKEY'
ENV['THIRTY_SEVEN_SIGNALS_OAUTH_SECRET'] = 'TESTSECRET'
@service.refresh_token = 'refreshtokentest'
@service.refresh_token!
@service.token.should == 'NEWTOKEN'

View file

@ -1,4 +1,3 @@
# This file is copied to spec/ when you run 'rails generate rspec:install'
ENV["RAILS_ENV"] ||= 'test'
if ENV['COVERAGE']
@ -9,6 +8,10 @@ else
Coveralls.wear!('rails')
end
# Required ENV variables that are normally set in .env are setup here for the test environment.
require 'dotenv'
Dotenv.load File.join(File.dirname(__FILE__), "env.test")
require File.expand_path("../../config/environment", __FILE__)
require 'rspec/rails'
require 'rspec/autorun'
@ -19,7 +22,7 @@ WebMock.disable_net_connect!
# Requires supporting ruby files with custom matchers and macros, etc,
# in spec/support/ and its subdirectories.
Dir[Rails.root.join("spec/support/**/*.rb")].each {|f| require f}
Dir[Rails.root.join("spec/support/**/*.rb")].each { |f| require f }
ActiveRecord::Migration.maintain_test_schema!