@@ -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 |