| @@ -37,6 +37,7 @@ gem 'haversine' | ||
| 37 | 37 | # Optional Services. | 
| 38 | 38 | gem 'omniauth-37signals' # BasecampAgent | 
| 39 | 39 | # gem 'omniauth-github' | 
| 40 | +gem 'omniauth-wunderlist', github: 'wunderlist/omniauth-wunderlist', ref: 'd0910d0396107b9302aa1bc50e74bb140990ccb8' | |
| 40 | 41 |  | 
| 41 | 42 | # Bundler <1.5 does not recognize :x64_mingw as a valid platform name. | 
| 42 | 43 | # Unfortunately, it can't self-update because it errors when encountering :x64_mingw. | 
| @@ -8,6 +8,15 @@ GIT | ||
| 8 | 8 | http_parser.rb (~> 0.6.0) | 
| 9 | 9 | simple_oauth (~> 0.2.0) | 
| 10 | 10 |  | 
| 11 | +GIT | |
| 12 | + remote: git://github.com/wunderlist/omniauth-wunderlist.git | |
| 13 | + revision: d0910d0396107b9302aa1bc50e74bb140990ccb8 | |
| 14 | + ref: d0910d0396107b9302aa1bc50e74bb140990ccb8 | |
| 15 | + specs: | |
| 16 | + omniauth-wunderlist (0.0.1) | |
| 17 | + omniauth (~> 1.0) | |
| 18 | + omniauth-oauth2 (~> 1.1) | |
| 19 | + | |
| 11 | 20 | GEM | 
| 12 | 21 | remote: https://rubygems.org/ | 
| 13 | 22 | specs: | 
| @@ -523,6 +532,7 @@ DEPENDENCIES | ||
| 523 | 532 | omniauth-dropbox | 
| 524 | 533 | omniauth-tumblr | 
| 525 | 534 | omniauth-twitter | 
| 535 | + omniauth-wunderlist! | |
| 526 | 536 | pg | 
| 527 | 537 | protected_attributes (~> 1.0.8) | 
| 528 | 538 | pry | 
| @@ -252,8 +252,8 @@ h2 .scenario, a span.label.scenario { | ||
| 252 | 252 | width: 200px; | 
| 253 | 253 | } | 
| 254 | 254 |  | 
| 255 | -$services: twitter 37signals github tumblr dropbox; | |
| 256 | -$service-colors: #55acee #8fc857 #444444 #2c4762 #007EE5; | |
| 255 | +$services: twitter 37signals github tumblr dropbox wunderlist; | |
| 256 | +$service-colors: #55acee #8fc857 #444444 #2c4762 #007EE5 #ED5F27; | |
| 257 | 257 |  | 
| 258 | 258 |  @mixin services { | 
| 259 | 259 |    @each $service in $services { | 
| @@ -57,6 +57,8 @@ module ApplicationHelper | ||
| 57 | 57 | case provider.to_sym | 
| 58 | 58 | when :twitter, :tumblr, :github, :dropbox | 
| 59 | 59 |        icon_tag("fa-#{provider}") | 
| 60 | + when :wunderlist | |
| 61 | +      icon_tag("fa-list") | |
| 60 | 62 | else | 
| 61 | 63 |        icon_tag("fa-lock") | 
| 62 | 64 | end | 
| @@ -0,0 +1,78 @@ | ||
| 1 | +module Agents | |
| 2 | + class WunderlistAgent < Agent | |
| 3 | + include FormConfigurable | |
| 4 | + include Oauthable | |
| 5 | + valid_oauth_providers :wunderlist | |
| 6 | + | |
| 7 | + cannot_be_scheduled! | |
| 8 | + | |
| 9 | +    gem_dependency_check { Devise.omniauth_providers.include?(:wunderlist) } | |
| 10 | + | |
| 11 | + description <<-MD | |
| 12 | +      #{'## Include the `omniauth-wunderlist` gem in your `Gemfile` and set `WUNDERLIST_OAUTH_KEY` and `WUNDERLIST_OAUTH_SECRET` in your environment to use this Agent' if dependencies_missing?} | |
| 13 | + | |
| 14 | + The WunderlistAgent creates new new tasks based on the incoming event. | |
| 15 | + | |
| 16 | + To be able to use this Agent you need to authenticate with Wunderlist in the [Services](/services) section first. | |
| 17 | + | |
| 18 | + MD | |
| 19 | + | |
| 20 | + def default_options | |
| 21 | +      { | |
| 22 | + 'list_id' => '', | |
| 23 | +        'title' => '{{title}}' | |
| 24 | + } | |
| 25 | + end | |
| 26 | + | |
| 27 | + form_configurable :list_id, roles: :completable | |
| 28 | + form_configurable :title | |
| 29 | + | |
| 30 | + def complete_list_id | |
| 31 | + response = request_guard do | |
| 32 | + HTTParty.get lists_url, request_options | |
| 33 | + end | |
| 34 | +      response.map { |p| {text: "#{p['title']} (#{p['id']})", id: p['id']}} | |
| 35 | + end | |
| 36 | + | |
| 37 | + def validate_options | |
| 38 | + errors.add(:base, "you need to specify the list you want to add tasks to") unless options['list_id'].present? | |
| 39 | + errors.add(:base, "you need to specify the title of the task to create") unless options['title'].present? | |
| 40 | + end | |
| 41 | + | |
| 42 | + def working? | |
| 43 | + !recent_error_logs? | |
| 44 | + end | |
| 45 | + | |
| 46 | + def receive(incoming_events) | |
| 47 | + incoming_events.each do |event| | |
| 48 | + mo = interpolated(event) | |
| 49 | + title = mo[:title][0..244] | |
| 50 | +        log("Creating new task '#{title}' on list #{mo[:list_id]}", inbound_event: event) | |
| 51 | + request_guard do | |
| 52 | +          HTTParty.post tasks_url, request_options.merge(body: {title: title, list_id: mo[:list_id].to_i}.to_json) | |
| 53 | + end | |
| 54 | + end | |
| 55 | + end | |
| 56 | + private | |
| 57 | + def request_guard(&blk) | |
| 58 | + response = yield | |
| 59 | +      error("Error during http request: #{response.body}") if response.code > 400 | |
| 60 | + response | |
| 61 | + end | |
| 62 | + | |
| 63 | + def lists_url | |
| 64 | + "https://a.wunderlist.com/api/v1/lists" | |
| 65 | + end | |
| 66 | + | |
| 67 | + def tasks_url | |
| 68 | + "https://a.wunderlist.com/api/v1/tasks" | |
| 69 | + end | |
| 70 | + | |
| 71 | + def request_options | |
| 72 | +      {:headers => {'Content-Type' => 'application/json', | |
| 73 | + 'User-Agent' => 'Huginn (https://github.com/cantino/huginn)', | |
| 74 | + 'X-Access-Token' => service.token, | |
| 75 | + 'X-Client-ID' => ENV["WUNDERLIST_OAUTH_KEY"] }} | |
| 76 | + end | |
| 77 | + end | |
| 78 | +end | 
| @@ -263,6 +263,12 @@ Devise.setup do |config| | ||
| 263 | 263 | config.omniauth :dropbox, key, secret | 
| 264 | 264 | end | 
| 265 | 265 |  | 
| 266 | + if defined?(OmniAuth::Strategies::Wunderlist) && | |
| 267 | + (key = ENV["WUNDERLIST_OAUTH_KEY"]).present? && | |
| 268 | + (secret = ENV["WUNDERLIST_OAUTH_SECRET"]).present? | |
| 269 | + config.omniauth :wunderlist, key, secret | |
| 270 | + end | |
| 271 | + | |
| 266 | 272 | # ==> Warden configuration | 
| 267 | 273 | # If you want to use other strategies, that are not supported by Devise, or | 
| 268 | 274 | # change the failure app, you can configure them inside the config.warden block. | 
| @@ -32,6 +32,7 @@ en: | ||
| 32 | 32 | github: "GitHub" | 
| 33 | 33 | 37signals: "37Signals (Basecamp)" | 
| 34 | 34 | dropbox: "Dropbox" | 
| 35 | + wunderlist: 'Wunderlist' | |
| 35 | 36 | passwords: | 
| 36 | 37 | no_token: "You can't access this page without coming from a password reset email. If you do come from a password reset email, please make sure you used the full URL provided." | 
| 37 | 38 | send_instructions: "You will receive an email with instructions on how to reset your password in a few minutes." | 
| @@ -7,4 +7,5 @@ THIRTY_SEVEN_SIGNALS_OAUTH_KEY=TESTKEY | ||
| 7 | 7 | THIRTY_SEVEN_SIGNALS_OAUTH_SECRET=TESTSECRET | 
| 8 | 8 | DROPBOX_OAUTH_KEY=dropboxoauthkey | 
| 9 | 9 | DROPBOX_OAUTH_SECRET=dropboxoauthsecret | 
| 10 | +WUNDERLIST_OAUTH_KEY=wunderoauthkey | |
| 10 | 11 | FAILED_JOBS_TO_KEEP=2 | 
| @@ -0,0 +1,74 @@ | ||
| 1 | +require 'spec_helper' | |
| 2 | +require 'models/concerns/oauthable' | |
| 3 | + | |
| 4 | +describe Agents::WunderlistAgent do | |
| 5 | + it_behaves_like Oauthable | |
| 6 | + | |
| 7 | + before(:each) do | |
| 8 | + | |
| 9 | +    @valid_params = { | |
| 10 | + 'list_id' => '12345', | |
| 11 | +                      'title' => '{{title}}: {{url}}', | |
| 12 | + } | |
| 13 | + | |
| 14 | + @checker = Agents::WunderlistAgent.new(:name => "somename", :options => @valid_params) | |
| 15 | + @checker.user = users(:jane) | |
| 16 | + @checker.service = services(:generic) | |
| 17 | + @checker.save! | |
| 18 | + | |
| 19 | + @event = Event.new | |
| 20 | + @event.agent = agents(:bob_weather_agent) | |
| 21 | +    @event.payload = { title: 'hello', url: 'www.example.com'} | |
| 22 | + @event.save! | |
| 23 | + end | |
| 24 | + | |
| 25 | + describe "validating" do | |
| 26 | + before do | |
| 27 | + expect(@checker).to be_valid | |
| 28 | + end | |
| 29 | + | |
| 30 | + it "should require the title" do | |
| 31 | + @checker.options['title'] = nil | |
| 32 | + expect(@checker).not_to be_valid | |
| 33 | + end | |
| 34 | + | |
| 35 | + it "should require the list_id" do | |
| 36 | + @checker.options['list_id'] = nil | |
| 37 | + expect(@checker).not_to be_valid | |
| 38 | + end | |
| 39 | + end | |
| 40 | + | |
| 41 | + it "should generate the request_options" do | |
| 42 | +    expect(@checker.send(:request_options)).to eq({:headers=>{"Content-Type"=>"application/json", "User-Agent"=>"Huginn (https://github.com/cantino/huginn)", "X-Access-Token"=>"1234token", "X-Client-ID"=>"wunderoauthkey"}}) | |
| 43 | + end | |
| 44 | + | |
| 45 | + describe "#complete_list_id" do | |
| 46 | + it "should return a array of hashes" do | |
| 47 | + stub_request(:get, 'https://a.wunderlist.com/api/v1/lists').to_return( | |
| 48 | +        :body => JSON.dump([{title: 'test', id: 12345}]), | |
| 49 | +        :headers => {"Content-Type" => "text/json"} | |
| 50 | + ) | |
| 51 | +      expect(@checker.complete_list_id).to eq([{:text=>"test (12345)", :id=>12345}]) | |
| 52 | + end | |
| 53 | + end | |
| 54 | + | |
| 55 | + describe "#receive" do | |
| 56 | + it "send a message to the hipchat" do | |
| 57 | +      stub_request(:post, 'https://a.wunderlist.com/api/v1/tasks').with { |request| request.body == 'abc'} | |
| 58 | + @checker.receive([@event]) | |
| 59 | + end | |
| 60 | + end | |
| 61 | + | |
| 62 | + describe "#working?" do | |
| 63 | + it "should be working with no entry in the error log" do | |
| 64 | + expect(@checker).to be_working | |
| 65 | + end | |
| 66 | + | |
| 67 | + it "should not be working with a recent entry in the error log" do | |
| 68 | +      @checker.error("test") | |
| 69 | + @checker.reload | |
| 70 | + @checker.last_event_at = Time.now | |
| 71 | + expect(@checker).to_not be_working | |
| 72 | + end | |
| 73 | + end | |
| 74 | +end |