Przeglądaj źródła

Use `omniauth-dropbox` instead of requiring an api key per agent; And `dropbox-api` instead of rolling our own.

Guilherme J. Tramontina 10 lat temu
rodzic
commit
04a244661d

+ 5 - 2
.env.example

@@ -53,8 +53,8 @@ INVITATION_CODE=try-huginn
 
 # Outgoing email settings.  To use Gmail or Google Apps, put your Google Apps domain or gmail.com
 # as the SMTP_DOMAIN and your Gmail username and password as the SMTP_USER_NAME and SMTP_PASSWORD.
-# 
-# PLEASE NOTE: In order to enable emails locally (e.g., when not in the production Rails environment), 
+#
+# PLEASE NOTE: In order to enable emails locally (e.g., when not in the production Rails environment),
 # you must also change config.action_mailer.perform_deliveries in config/environments/development.rb.
 
 SMTP_DOMAIN=your-domain-here.com
@@ -92,6 +92,9 @@ GITHUB_OAUTH_SECRET=
 TUMBLR_OAUTH_KEY=
 TUMBLR_OAUTH_SECRET=
 
+DROPBOX_OAUTH_KEY=
+DROPBOX_OAUTH_SECRET=
+
 #############################
 #  AWS and Mechanical Turk  #
 #############################

+ 4 - 0
Gemfile

@@ -26,6 +26,10 @@ gem 'omniauth-twitter'
 gem 'tumblr_client'
 gem 'omniauth-tumblr'
 
+# Dropbox Agents
+gem 'dropbox-api'
+gem 'omniauth-dropbox'
+
 # Optional Services.
 gem 'omniauth-37signals'          # BasecampAgent
 # gem 'omniauth-github'

+ 8 - 0
Gemfile.lock

@@ -95,6 +95,10 @@ GEM
     dotenv-deployment (0.0.2)
     dotenv-rails (0.11.1)
       dotenv (= 0.11.1)
+    dropbox-api (0.4.2)
+      hashie
+      multi_json
+      oauth
     em-http-request (1.1.2)
       addressable (>= 2.3.4)
       cookiejar
@@ -206,6 +210,8 @@ GEM
     omniauth-37signals (1.0.5)
       omniauth (~> 1.0)
       omniauth-oauth2 (~> 1.0)
+    omniauth-dropbox (0.2.0)
+      omniauth-oauth (~> 1.0)
     omniauth-oauth (1.0.1)
       oauth
       omniauth (~> 1.0)
@@ -417,6 +423,7 @@ DEPENDENCIES
   devise (~> 3.2.4)
   dotenv-deployment
   dotenv-rails
+  dropbox-api
   em-http-request (~> 1.1.2)
   faraday (~> 0.9.0)
   faraday_middleware
@@ -443,6 +450,7 @@ DEPENDENCIES
   nokogiri (~> 1.6.1)
   omniauth
   omniauth-37signals
+  omniauth-dropbox
   omniauth-tumblr
   omniauth-twitter
   pg

+ 5 - 0
app/assets/stylesheets/application.css.scss.erb

@@ -273,4 +273,9 @@ h2 .scenario, a span.label.scenario {
     color: #fff;
     background-color: #444;
   }
+
+  &.btn-auth-dropbox {
+    color: #fff;
+    background-color: #007EE5;
+  }
 }

+ 35 - 0
app/concerns/dropbox_concern.rb

@@ -0,0 +1,35 @@
+module DropboxConcern
+  extend ActiveSupport::Concern
+
+  included do
+    include Oauthable
+    valid_oauth_providers :dropbox
+    gem_dependency_check { defined?(Dropbox) && Devise.omniauth_providers.include?(:dropbox) }
+  end
+
+  def dropbox
+    Dropbox::API::Config.app_key = consumer_key
+    Dropbox::API::Config.app_secret = consumer_secret
+    Dropbox::API::Config.mode = 'dropbox'
+    Dropbox::API::Client.new(token: oauth_token, secret: oauth_token_secret)
+  end
+
+  private
+
+  def consumer_key
+    (config = Devise.omniauth_configs[:dropbox]) && config.strategy.consumer_key
+  end
+
+  def consumer_secret
+    (config = Devise.omniauth_configs[:dropbox]) && config.strategy.consumer_secret
+  end
+
+  def oauth_token
+    service && service.token
+  end
+
+  def oauth_token_secret
+    service && service.secret
+  end
+
+end

+ 1 - 1
app/helpers/application_helper.rb

@@ -43,7 +43,7 @@ module ApplicationHelper
 
   def icon_for_service(service)
     case service.to_sym
-    when :twitter, :tumblr, :github
+    when :twitter, :tumblr, :github, :dropbox
       "<i class='fa fa-#{service}'></i>".html_safe
     else
       "<i class='fa fa-lock'></i>".html_safe

+ 12 - 29
app/models/agents/dropbox_watch_agent.rb

@@ -1,11 +1,13 @@
 module Agents
   class DropboxWatchAgent < Agent
+    include DropboxConcern
+
     cannot_receive_events!
     default_schedule "every_1m"
 
     description <<-MD
+      #{'## Include the `dropbox-api` and `omniauth-dropbox` gems in your `Gemfile` and set `DROPBOX_OAUTH_KEY` and `DROPBOX_OAUTH_SECRET` in your environment to use Dropbox Agents.' if dependencies_missing?}
       The _DropboxWatchAgent_ watches the given `dir_to_watch` and emits events with the detected changes.
-      It requires a [Dropbox App](https://www.dropbox.com/developers/apps) and its `access_token`, which will be used to authenticate on your account.
     MD
 
     event_description <<-MD
@@ -24,14 +26,12 @@ module Agents
 
     def default_options
       {
-        'access_token' => 'your_dropbox_app_access_token',
         'dir_to_watch' => '/',
         'expected_update_period_in_days' => 1
       }
     end
 
     def validate_options
-      errors.add(:base, 'The `access_token` property is required.') unless options['access_token'].present?
       errors.add(:base, 'The `dir_to_watch` property is required.') unless options['dir_to_watch'].present?
       errors.add(:base, 'Invalid `expected_update_period_in_days` format.') unless options['expected_update_period_in_days'].present? && is_positive_integer?(options['expected_update_period_in_days'])
     end
@@ -41,8 +41,7 @@ module Agents
     end
 
     def check
-      api = DropboxAPI.new(interpolated['access_token'])
-      current_contents = api.dir(interpolated['dir_to_watch'])
+      current_contents = ls(interpolated['dir_to_watch'])
       diff = DropboxDirDiff.new(previous_contents, current_contents)
       create_event(payload: diff.to_hash) unless previous_contents.nil? || diff.empty?
 
@@ -57,6 +56,14 @@ module Agents
       false
     end
 
+    def ls(dir_to_watch)
+      dropbox.ls(dir_to_watch).map { |entry| slice_json(entry, 'path', 'rev', 'modified') }
+    end
+
+    def slice_json(json, *keys)
+      keys.each_with_object({}){|key, hash| hash[key.to_s] = json[key.to_s]}
+    end
+
     def previous_contents
       self.memory['contents']
     end
@@ -67,30 +74,6 @@ module Agents
 
     # == Auxiliary classes ==
 
-    class DropboxAPI
-      class ResourceNotFound < RuntimeError; end
-
-      include HTTParty
-      base_uri 'https://api.dropbox.com/1'
-
-      def initialize(access_token)
-        @options = { query: { access_token: access_token } }
-      end
-
-      def dir(to_watch)
-        options = @options.deep_merge({ query: { list: true } })
-        response = self.class.get("/metadata/auto#{to_watch}", options)
-        raise ResourceNotFound.new(to_watch) if response.not_found?
-        JSON.parse(response)['contents'].map { |entry| slice_json(entry, 'path', 'rev', 'modified') }
-      end
-
-      private
-
-      def slice_json(json, *keys)
-        keys.each_with_object({}){|key, hash| hash[key.to_s] = json[key.to_s]}
-      end
-    end
-
     class DropboxDirDiff
       def initialize(previous, current)
         @previous, @current = [previous || [], current || []]

+ 1 - 3
app/models/service.rb

@@ -59,12 +59,10 @@ class Service < ActiveRecord::Base
 
   def self.provider_specific_options(omniauth)
     case omniauth['provider'].to_sym
-      when :twitter, :github
-        { name: omniauth['info']['nickname'] }
       when :'37signals'
         { user_id: omniauth['extra']['accounts'][0]['id'], name: omniauth['info']['name'] }
       else
-        { name: omniauth['info']['nickname'] }
+        { name: omniauth['info']['nickname'] || omniauth['info']['name'] }
     end
   end
 

+ 8 - 1
config/initializers/devise.rb

@@ -136,7 +136,7 @@ Devise.setup do |config|
   # The time you want to timeout the user session without activity. After this
   # time the user will be asked for credentials again. Default is 30 minutes.
   # config.timeout_in = 30.minutes
-  
+
   # If true, expires auth token on session timeout.
   # config.expire_auth_token_on_timeout = false
 
@@ -213,6 +213,7 @@ Devise.setup do |config|
   # Add a new OmniAuth provider. Check the wiki for more information on setting
   # up on your models and hooks.
   # config.omniauth :github, 'APP_ID', 'APP_SECRET', :scope => 'user,public_repo'
+
   if defined?(OmniAuth::Strategies::Twitter) &&
      (key = ENV["TWITTER_OAUTH_KEY"]).present? &&
      (secret = ENV["TWITTER_OAUTH_SECRET"]).present?
@@ -237,6 +238,12 @@ Devise.setup do |config|
     config.omniauth :github, key, secret
   end
 
+  if defined?(OmniAuth::Strategies::Dropbox) &&
+     (key = ENV["DROPBOX_OAUTH_KEY"]).present? &&
+     (secret = ENV["DROPBOX_OAUTH_SECRET"]).present?
+    config.omniauth :dropbox, key, secret
+  end
+
   # ==> Warden configuration
   # If you want to use other strategies, that are not supported by Devise, or
   # change the failure app, you can configure them inside the config.warden block.

+ 1 - 0
config/locales/devise.en.yml

@@ -54,6 +54,7 @@ en:
       tumblr: 'Tumblr'
       github: 'GitHub'
       37signals: '37Signals (Basecamp)'
+      dropbox: 'Dropbox'
     mailer:
       confirmation_instructions:
         subject: 'Confirmation instructions'

+ 7 - 75
spec/models/agents/dropbox_watch_agent_spec.rb

@@ -11,6 +11,7 @@ describe Agents::DropboxWatchAgent do
       }
     )
     @agent.user = users(:bob)
+    @agent.service = services(:generic)
     @agent.save!
   end
 
@@ -29,11 +30,6 @@ describe Agents::DropboxWatchAgent do
   describe '#valid?' do
     before(:each) { expect(@agent.valid?).to eq true }
 
-    it 'requires the "access_token"' do
-      @agent.options[:access_token] = nil
-      expect(@agent.valid?).to eq false
-    end
-
     it 'requires a "dir_to_watch"' do
       @agent.options[:dir_to_watch] = nil
       expect(@agent.valid?).to eq false
@@ -54,11 +50,11 @@ describe Agents::DropboxWatchAgent do
 
   describe '#check' do
 
-    let(:first_result) { 'first_result' }
+    let(:first_result) { [{ 'path' => '1.json', 'rev' => '1', 'modified' => '01-01-01' }] }
 
     before(:each) do
-      stub.proxy(Agents::DropboxWatchAgent::DropboxAPI).new('70k3n') do |api|
-        stub(api).dir('/my/dropbox/dir') { first_result }
+      stub.proxy(Dropbox::API::Client).new do |api|
+        stub(api).ls('/my/dropbox/dir') { first_result }
       end
     end
 
@@ -79,13 +75,13 @@ describe Agents::DropboxWatchAgent do
 
     context 'subsequent calls' do
 
-      let(:second_result) { 'second_result' }
+      let(:second_result) { [{ 'path' => '2.json', 'rev' => '1', 'modified' => '02-02-02' }] }
 
       before(:each) do
         @agent.memory = { 'contents' => 'not_empty' }
 
-        stub.proxy(Agents::DropboxWatchAgent::DropboxAPI).new('70k3n') do |api|
-          stub(api).dir('/my/dropbox/dir') { second_result }
+        stub.proxy(Dropbox::API::Client).new do |api|
+          stub(api).ls('/my/dropbox/dir') { second_result }
         end
       end
 
@@ -174,68 +170,4 @@ describe Agents::DropboxWatchAgent do
     end
   end
 
-  describe Agents::DropboxWatchAgent::DropboxAPI do
-    let(:dir_to_watch) { '/my/dropbox/dir' }
-    let(:access_token) { '70k3n' }
-    let(:api_url) { "https://api.dropbox.com/1/metadata/auto#{dir_to_watch}?access_token=#{access_token}&list=true" }
-
-    describe '#dir' do
-
-      context 'when the provided path exists' do
-        before do
-          stub_request(:get, api_url).to_return(body: JSON.dump({
-            contents: [
-                {
-                    bytes: 0,
-                    icon: "folder",
-                    is_dir: true,
-                    modified: "Mon, 11 Mar 2013 15:41:44 +0000",
-                    path: "#{dir_to_watch}/1.json",
-                    rev: "1",
-                    revision: 14743,
-                    root: "dropbox",
-                    size: "0 bytes",
-                    thumb_exists: false
-                },
-                {
-                    bytes: 0,
-                    icon: "folder",
-                    is_dir: true,
-                    modified: "Mon, 12 Mar 2013 15:41:44 +0000",
-                    path: "#{dir_to_watch}/2.json",
-                    rev: "4",
-                    revision: 113022,
-                    root: "dropbox",
-                    size: "0 bytes",
-                    thumb_exists: false
-                }
-            ],
-            some: "other",
-            things: "we",
-            dont: "need"
-          }))
-        end
-
-        it 'trims down the attributes of the response to our needs' do
-          dir_list = Agents::DropboxWatchAgent::DropboxAPI.new(access_token).dir(dir_to_watch)
-          expect(dir_list).to eq [
-            { 'path' => "#{dir_to_watch}/1.json", 'rev' => '1', 'modified' => 'Mon, 11 Mar 2013 15:41:44 +0000' },
-            { 'path' => "#{dir_to_watch}/2.json", 'rev' => '4', 'modified' => 'Mon, 12 Mar 2013 15:41:44 +0000' }
-          ]
-        end
-      end
-
-      context 'when the provided path does not exist' do
-        before { stub_request(:get, api_url).to_return(status: 404, body: '{"error": "Not Found"}') }
-
-        it 'raises a ResourceNotFound error' do
-          expect {
-            Agents::DropboxWatchAgent::DropboxAPI.new(access_token).dir(dir_to_watch)
-          }.to raise_error(Agents::DropboxWatchAgent::DropboxAPI::ResourceNotFound, dir_to_watch)
-        end
-      end
-
-    end
-
-  end
 end