Browse Source

Merge pull request #2047 from albertsun/update-google-api-client

Update google-api-client and Google agents
Akinori MUSHA 7 years ago
parent
commit
cec6ce1a56

+ 1 - 0
CHANGES.md

@@ -2,6 +2,7 @@
 
 | DateOfChange   | Changes                                                                                                      |
 |----------------|--------------------------------------------------------------------------------------------------------------|
+| Jul 10, 2017   | Update Google API Client. May break backwards compatibility for GoogleCalendarPublishAgent. [2047](https://github.com/huginn/huginn/pull/2047)   |
 | Oct 17, 2016   | Normalize URL in `to_uri` and `uri_expand` liquid filters.                                                   |
 | Oct 06, 2016   | `RssAgent` is reimplemented migrating its underlying feed parser from FeedNormalizer to Feedjira. [1564](https://github.com/cantino/huginn/pull/1564)     |
 | Oct 05, 2016   | Migrate to Rails 5. [1688](https://github.com/cantino/huginn/pull/1688)                                      |

+ 7 - 5
Gemfile

@@ -44,11 +44,12 @@ gem 'slack-notifier', '~> 1.0.0'  # SlackAgent
 gem 'hypdf', '~> 1.0.10'          # PDFInfoAgent
 
 # Weibo Agents
-# FIXME needs to loosen omniauth dependency
-gem 'weibo_2', github: 'dsander/weibo_2', branch: 'master'
+# FIXME needs to loosen omniauth dependency, add rest-client
+gem 'weibo_2', github: 'albertsun/weibo_2', branch: 'master'
 
 # GoogleCalendarPublishAgent and GoogleTranslateAgent
-gem 'google-api-client', '~> 0.7.1', require: 'google/api_client'
+gem 'google-api-client', '~> 0.13'
+gem 'google-cloud-translate', '~> 1.0.0', require: 'google/cloud/translate'
 
 # Twitter Agents
 gem 'twitter', github: 'sferik/twitter' # Must to be loaded before cantino-twitter-stream.
@@ -56,7 +57,8 @@ gem 'twitter-stream', github: 'cantino/twitter-stream', branch: 'huginn'
 gem 'omniauth-twitter', '~> 1.3.0'
 
 # Tumblr Agents
-gem 'tumblr_client', github: 'tumblr/tumblr_client', branch: 'master', ref: '0c59b04e49f2a8c89860613b18cf4e8f978d8dc7'  # '>= 0.8.5'
+# until merge of https://github.com/tumblr/tumblr_client/pull/61
+gem 'tumblr_client', github: 'albertsun/tumblr_client', branch: 'master', ref: 'e046fe6e39291c173add0a49081630c7b60a36c7' 
 gem 'omniauth-tumblr', '~> 1.2'
 
 # Dropbox Agents
@@ -147,7 +149,7 @@ group :development do
   end
 
   group :test do
-    gem 'coveralls', '~> 0.7.4', require: false
+    gem 'coveralls', '~> 0.8.12', require: false
     gem 'capybara', '~> 2.13.0'
     gem 'capybara-screenshot'
     gem 'capybara-select2', require: false

+ 99 - 76
Gemfile.lock

@@ -1,3 +1,28 @@
+GIT
+  remote: https://github.com/albertsun/tumblr_client.git
+  revision: e046fe6e39291c173add0a49081630c7b60a36c7
+  ref: e046fe6e39291c173add0a49081630c7b60a36c7
+  branch: master
+  specs:
+    tumblr_client (0.8.5)
+      faraday (<= 1.0.0)
+      faraday_middleware (<= 1.0.0)
+      json
+      mime-types
+      oauth
+      simple_oauth
+
+GIT
+  remote: https://github.com/albertsun/weibo_2.git
+  revision: ac38d04434747c4b88e86c5337cd436d00c34349
+  branch: master
+  specs:
+    weibo_2 (0.1.7)
+      hashie (~> 3)
+      multi_json (~> 1)
+      oauth2 (~> 1)
+      rest-client (>= 2.0)
+
 GIT
   remote: https://github.com/amatsuda/kaminari.git
   revision: abbf93d557208ee1d0b612c612cd079f86ed54f4
@@ -35,17 +60,6 @@ GIT
       activerecord (>= 3.0, < 5.2)
       delayed_job (>= 3.0, < 5)
 
-GIT
-  remote: https://github.com/dsander/weibo_2.git
-  revision: e5b77f21a7e9a666b582c48e16b1e96fca198cf8
-  branch: master
-  specs:
-    weibo_2 (0.1.7)
-      hashie (~> 3)
-      multi_json (~> 1)
-      oauth2 (~> 1)
-      rest-client (~> 1.8)
-
 GIT
   remote: https://github.com/lostisland/faraday_middleware.git
   revision: 59088da02940d0ee2010b2e3156343346767c31e
@@ -70,20 +84,6 @@ GIT
       naught (~> 1.0)
       simple_oauth (~> 0.3.0)
 
-GIT
-  remote: https://github.com/tumblr/tumblr_client.git
-  revision: 0c59b04e49f2a8c89860613b18cf4e8f978d8dc7
-  ref: 0c59b04e49f2a8c89860613b18cf4e8f978d8dc7
-  branch: master
-  specs:
-    tumblr_client (0.8.5)
-      faraday (~> 0.9.0)
-      faraday_middleware (~> 0.9)
-      json
-      mime-types
-      oauth
-      simple_oauth
-
 PATH
   remote: vendor/gems/dotenv-2.0.1
   specs:
@@ -135,10 +135,6 @@ GEM
     addressable (2.5.1)
       public_suffix (~> 2.0, >= 2.0.2)
     arel (8.0.0)
-    autoparse (0.3.3)
-      addressable (>= 2.3.1)
-      extlib (>= 0.9.15)
-      multi_json (>= 1.0.0)
     aws-sdk-core (2.2.15)
       jmespath (~> 1.0)
     bcrypt (3.1.11)
@@ -189,16 +185,18 @@ GEM
     colorize (0.7.7)
     concurrent-ruby (1.0.5)
     cookiejar (0.3.2)
-    coveralls (0.7.12)
-      multi_json (~> 1.10)
-      rest-client (>= 1.6.8, < 2)
-      simplecov (~> 0.9.1)
+    coveralls (0.8.21)
+      json (>= 1.8, < 3)
+      simplecov (~> 0.14.1)
       term-ansicolor (~> 1.3)
-      thor (~> 0.19.1)
+      thor (~> 0.19.4)
+      tins (~> 1.6)
     crack (0.4.3)
       safe_yaml (~> 1.0.0)
     daemons (1.1.9)
     debug_inspector (0.0.2)
+    declarative (0.0.9)
+    declarative-option (0.1.0)
     delorean (2.1.0)
       chronic
     devise (4.3.0)
@@ -209,7 +207,7 @@ GEM
       warden (~> 1.2.3)
     diff-lcs (1.3)
     docile (1.1.5)
-    domain_name (0.5.24)
+    domain_name (0.5.20170404)
       unf (>= 0.0.5, < 1.0.0)
     dropbox-api (0.4.2)
       hashie
@@ -239,8 +237,7 @@ GEM
       evernote-thrift
       oauth (>= 0.4.1)
     execjs (2.6.0)
-    extlib (0.9.16)
-    faraday (0.9.2)
+    faraday (0.12.1)
       multipart-post (>= 1.2, < 3)
     feedjira (2.1.0)
       faraday (>= 0.9)
@@ -265,17 +262,29 @@ GEM
       rails (>= 3.0)
     globalid (0.4.0)
       activesupport (>= 4.2.0)
-    google-api-client (0.7.1)
-      addressable (>= 2.3.2)
-      autoparse (>= 0.3.3)
-      extlib (>= 0.9.15)
-      faraday (>= 0.9.0)
-      jwt (>= 0.1.5)
-      launchy (>= 2.1.1)
-      multi_json (>= 1.0.0)
-      retriable (>= 1.4)
-      signet (>= 0.5.0)
-      uuidtools (>= 2.1.0)
+    google-api-client (0.13.0)
+      addressable (~> 2.5, >= 2.5.1)
+      googleauth (~> 0.5)
+      httpclient (>= 2.8.1, < 3.0)
+      mime-types (~> 3.0)
+      representable (~> 3.0)
+      retriable (>= 2.0, < 4.0)
+    google-cloud-core (1.0.0)
+      google-cloud-env (~> 1.0)
+      googleauth (~> 0.5.1)
+    google-cloud-env (1.0.0)
+      faraday (~> 0.11)
+    google-cloud-translate (1.0.0)
+      google-cloud-core (~> 1.0)
+      googleauth (~> 0.5.1)
+    googleauth (0.5.1)
+      faraday (~> 0.9)
+      jwt (~> 1.4)
+      logging (~> 2.0)
+      memoist (~> 0.12)
+      multi_json (~> 1.11)
+      os (~> 0.9)
+      signet (~> 0.7)
     guard (2.13.0)
       formatador (>= 0.2.4)
       listen (>= 2.7, <= 4.0)
@@ -296,7 +305,7 @@ GEM
       guard-compat (~> 1.1)
       rspec (>= 2.99.0, < 4.0)
     hashdiff (0.3.2)
-    hashie (3.4.6)
+    hashie (3.5.5)
     haversine (0.3.0)
     hipchat (1.2.0)
       httparty
@@ -309,12 +318,13 @@ GEM
       http-cookie (~> 1.0)
       http-form_data (~> 1.0.1)
       http_parser.rb (~> 0.6.0)
-    http-cookie (1.0.2)
+    http-cookie (1.0.3)
       domain_name (~> 0.5)
     http-form_data (1.0.1)
     http_parser.rb (0.6.0)
     httparty (0.14.0)
       multi_xml (>= 0.5.2)
+    httpclient (2.8.3)
     huginn_agent (0.4.0)
       thor
     hypdf (1.0.10)
@@ -329,7 +339,7 @@ GEM
     json (1.8.6)
     jsonpath (0.8.3)
       multi_json
-    jwt (1.4.1)
+    jwt (1.5.6)
     kgio (2.10.0)
     kramdown (1.3.3)
     launchy (2.4.2)
@@ -345,6 +355,10 @@ GEM
     listen (3.0.5)
       rb-fsevent (>= 0.9.3)
       rb-inotify (>= 0.9)
+    little-plugger (1.1.4)
+    logging (2.2.2)
+      little-plugger (~> 1.1)
+      multi_json (~> 1.10)
     loofah (2.0.3)
       nokogiri (>= 1.5.9)
     lumberjack (1.0.10)
@@ -352,17 +366,20 @@ GEM
       systemu (~> 2.6.2)
     mail (2.6.5)
       mime-types (>= 1.16, < 4)
+    memoist (0.16.0)
     memoizable (0.4.2)
       thread_safe (~> 0.3, >= 0.3.1)
     method_source (0.8.2)
-    mime-types (2.99.3)
+    mime-types (3.1)
+      mime-types-data (~> 3.2015)
+    mime-types-data (3.2016.0521)
     mimemagic (0.3.1)
     mini_magick (4.2.3)
     mini_portile2 (2.1.0)
     minitest (5.10.2)
     mqtt (0.3.1)
     multi_json (1.12.1)
-    multi_xml (0.5.5)
+    multi_xml (0.6.0)
     multipart-post (2.0.0)
     mysql2 (0.4.8)
     naught (1.1.0)
@@ -371,7 +388,7 @@ GEM
     net-scp (1.2.1)
       net-ssh (>= 2.6.5)
     net-ssh (3.0.2)
-    netrc (0.10.3)
+    netrc (0.11.0)
     nio4r (2.0.0)
     nokogiri (1.7.2)
       mini_portile2 (~> 2.1.0)
@@ -379,8 +396,8 @@ GEM
       nenv (~> 0.1)
       shellany (~> 0.0)
     oauth (0.4.7)
-    oauth2 (1.2.0)
-      faraday (>= 0.8, < 0.10)
+    oauth2 (1.4.0)
+      faraday (>= 0.8, < 0.13)
       jwt (~> 1.0)
       multi_json (~> 1.3)
       multi_xml (~> 0.5)
@@ -413,6 +430,7 @@ GEM
       omniauth (~> 1.0)
       omniauth-oauth2 (~> 1.1)
     orm_adapter (0.5.0)
+    os (0.9.6)
     pg (0.18.3)
     poltergeist (1.8.1)
       capybara (~> 2.1)
@@ -430,7 +448,7 @@ GEM
     pry-rails (0.3.4)
       pry (>= 0.9.10)
     public_suffix (2.0.5)
-    rack (2.0.2)
+    rack (2.0.3)
     rack-livereload (0.3.16)
       rack
     rack-test (0.6.3)
@@ -470,14 +488,18 @@ GEM
     rb-kqueue (0.2.4)
       ffi (>= 0.5.0)
     ref (2.0.0)
+    representable (3.0.4)
+      declarative (< 0.1.0)
+      declarative-option (< 0.2.0)
+      uber (< 0.2.0)
     responders (2.4.0)
       actionpack (>= 4.2.0, < 5.3)
       railties (>= 4.2.0, < 5.3)
-    rest-client (1.8.0)
+    rest-client (2.0.2)
       http-cookie (>= 1.0.2, < 2.0)
-      mime-types (>= 1.16, < 3.0)
-      netrc (~> 0.7)
-    retriable (2.0.2)
+      mime-types (>= 1.16, < 4.0)
+      netrc (~> 0.8)
+    retriable (3.0.2)
     rr (1.1.2)
     rspec (3.5.0)
       rspec-core (~> 3.5.0)
@@ -527,17 +549,17 @@ GEM
     shellany (0.0.1)
     shoulda-matchers (3.0.0)
       activesupport (>= 4.0.0)
-    signet (0.5.1)
-      addressable (>= 2.2.3)
-      faraday (>= 0.9.0.rc5)
-      jwt (>= 0.1.5)
-      multi_json (>= 1.0.0)
+    signet (0.7.3)
+      addressable (~> 2.3)
+      faraday (~> 0.9)
+      jwt (~> 1.5)
+      multi_json (~> 1.10)
     simple_oauth (0.3.1)
-    simplecov (0.9.2)
+    simplecov (0.14.1)
       docile (~> 1.1.0)
-      multi_json (~> 1.0)
-      simplecov-html (~> 0.9.0)
-    simplecov-html (0.9.0)
+      json (>= 1.8, < 3)
+      simplecov-html (~> 0.10.0)
+    simplecov-html (0.10.1)
     slack-notifier (1.0.0)
     slop (3.6.0)
     spectrum-rails (1.3.4)
@@ -560,7 +582,7 @@ GEM
       net-scp (>= 1.1.2)
       net-ssh (>= 2.8.0)
     systemu (2.6.4)
-    term-ansicolor (1.3.2)
+    term-ansicolor (1.6.0)
       tins (~> 1.0)
     therubyracer (0.12.3)
       libv8 (~> 3.16.14.15)
@@ -568,7 +590,7 @@ GEM
     thor (0.19.4)
     thread_safe (0.3.6)
     tilt (2.0.7)
-    tins (1.10.1)
+    tins (1.15.0)
     treetop (1.5.3)
       polyglot (~> 0.3)
     twilio-ruby (3.11.6)
@@ -579,18 +601,18 @@ GEM
       ethon (>= 0.7.1)
     tzinfo (1.2.3)
       thread_safe (~> 0.1)
+    uber (0.1.0)
     uglifier (2.7.2)
       execjs (>= 0.3.0)
       json (>= 1.8.0)
     unf (0.1.4)
       unf_ext
-    unf_ext (0.0.7.3)
+    unf_ext (0.0.7.4)
     unicorn (5.1.0)
       kgio (~> 2.6)
       raindrops (~> 0.7)
     uuid (2.3.7)
       macaddr (~> 1.0)
-    uuidtools (2.1.5)
     vcr (3.0.3)
     warden (1.2.7)
       rack (>= 1.0)
@@ -631,7 +653,7 @@ DEPENDENCIES
   capybara-screenshot
   capybara-select2
   coffee-rails (~> 4.2)
-  coveralls (~> 0.7.4)
+  coveralls (~> 0.8.12)
   daemons (~> 1.1.9)
   delayed_job!
   delayed_job_active_record!
@@ -651,7 +673,8 @@ DEPENDENCIES
   foreman (~> 0.63.0)
   geokit (~> 1.8.4)
   geokit-rails (~> 2.2.0)
-  google-api-client (~> 0.7.1)
+  google-api-client (~> 0.13)
+  google-cloud-translate (~> 1.0.0)
   guard (~> 2.13.0)
   guard-livereload (~> 2.5.1)
   guard-rspec (~> 4.6.4)

+ 7 - 0
app/controllers/application_controller.rb

@@ -33,6 +33,7 @@ class ApplicationController < ActionController::Base
     twitter_oauth_check
     basecamp_auth_check
     outdated_docker_image_namespace_check
+    outdated_google_auth_check
   end
 
   def filtered_agent_return_link(options = {})
@@ -70,6 +71,12 @@ class ApplicationController < ActionController::Base
     @outdated_docker_image_namespace = ENV['OUTDATED_DOCKER_IMAGE_NAMESPACE'] == 'true'
   end
 
+  def outdated_google_auth_check
+    @outdated_google_cal_agents = current_user.agents.of_type('Agents::GoogleCalendarPublishAgent').select do |agent|
+      agent.options['google']['key_secret'].present?
+    end
+  end
+
   def agent_params
     return {} unless params[:agent]
     @agent_params ||= begin

+ 46 - 20
app/models/agents/google_calendar_publish_agent.rb

@@ -1,11 +1,12 @@
 require 'json'
+require 'google/apis/calendar_v3'
 
 module Agents
   class GoogleCalendarPublishAgent < Agent
     cannot_be_scheduled!
     no_bulk_receive!
 
-    gem_dependency_check { defined?(Google) && defined?(Google::APIClient) }
+    gem_dependency_check { defined?(Google) && defined?(Google::Apis::CalendarV3) }
 
     description <<-MD
       The Google Calendar Publish Agent creates events on your Google Calendar.
@@ -20,9 +21,23 @@ module Agents
       2. New project -> Huginn
       3. APIs & Auth -> Enable google calendar
       4. Credentials -> Create new Client ID -> Service Account
-      5. Persist the generated private key to a path, ie: `/home/huginn/a822ccdefac89fac6330f95039c492dfa3ce6843.p12`
+      5. Download the JSON keyfile and save it to a path, ie: `/home/huginn/Huginn-5d12345678cd.json`. Or open that file and copy the `private_key`.
       6. Grant access via google calendar UI to the service account email address for each calendar you wish to manage. For a whole google apps domain, you can [delegate authority](https://developers.google.com/+/domains/authentication/delegation)
 
+      An earlier version of Huginn used PKCS12 key files to authenticate. This will no longer work, you should generate a new JSON format keyfile, that will look something like:
+      <pre><code>{
+        "type": "service_account",
+        "project_id": "huginn-123123",
+        "private_key_id": "6d6b476fc6ccdb31e0f171991e5528bb396ffbe4",
+        "private_key": "-----BEGIN PRIVATE KEY-----\\n...\\n-----END PRIVATE KEY-----\\n",
+        "client_email": "huginn-calendar@huginn-123123.iam.gserviceaccount.com",
+        "client_id": "123123...123123",
+        "auth_uri": "https://accounts.google.com/o/oauth2/auth",
+        "token_uri": "https://accounts.google.com/o/oauth2/token",
+        "auth_provider_x509_cert_url": "https://www.googleapis.com/oauth2/v1/certs",
+        "client_x509_cert_url": "https://www.googleapis.com/robot/v1/metadata/x509/huginn-calendar%40huginn-123123.iam.gserviceaccount.com"
+      }</code></pre>
+
 
       Agent Configuration:
 
@@ -30,12 +45,9 @@ module Agents
 
       `google` A hash of configuration options for the agent.
 
-      `google` `service_account_email` - The authorised service account.
-
-      `google` `key_file` OR `google` `key` - The path to the key file or the key itself.  [Liquid](https://github.com/cantino/huginn/wiki/Formatting-Events-using-Liquid) formatting is supported if you want to use a Credential.  (E.g., `{% credential google_key %}`)
-
-      `google` `key_secret` - The secret for the key, typically 'notasecret'
+      `google` `service_account_email` - The authorised service account email address.
 
+      `google` `key_file` OR `google` `key` - The path to the JSON key file above, or the key itself (the value of `private_key`).  [Liquid](https://github.com/cantino/huginn/wiki/Formatting-Events-using-Liquid) formatting is supported if you want to use a Credential.  (E.g., `{% credential google_key %}`)
 
       Set `expected_update_period_in_days` to the maximum amount of time that you'd expect to pass between Events being created by this Agent.
 
@@ -43,6 +55,8 @@ module Agents
 
       A hash of event details. See the [Google Calendar API docs](https://developers.google.com/google-apps/calendar/v3/reference/events/insert)
 
+      The prior version Google's API expected keys like `dateTime` but in the latest version they expect snake case keys like `date_time`.
+
       Example payload for trigger agent:
       <pre><code>{
         "message": {
@@ -50,10 +64,10 @@ module Agents
           "summary": "Awesome event",
           "description": "An example event with text. Pro tip: DateTimes are in RFC3339",
           "start": {
-            "dateTime": "2014-10-02T10:00:00-05:00"
+            "date_time": "2017-06-30T17:00:00-05:00"
           },
           "end": {
-            "dateTime": "2014-10-02T11:00:00-05:00"
+            "date_time": "2017-06-30T18:00:00-05:00"
           }
         }
       }</code></pre>
@@ -84,7 +98,7 @@ module Agents
         'calendar_id' => 'you@email.com',
         'google' => {
           'key_file' => '/path/to/private.key',
-          'key_secret' => 'notasecret',
+          'key' => '-----BEGIN PRIVATE KEY-----\n...\n-----END PRIVATE KEY-----\n',
           'service_account_email' => ''
         }
       }
@@ -93,16 +107,28 @@ module Agents
     def receive(incoming_events)
       require 'google_calendar'
       incoming_events.each do |event|
-        calendar = GoogleCalendar.new(interpolate_options(options, event), Rails.logger)
-
-        calendar_event = JSON.parse(calendar.publish_as(interpolated(event)['calendar_id'], event.payload["message"]).response.body)
-
-        create_event :payload => {
-          'success' => true,
-          'published_calendar_event' => calendar_event,
-          'agent_id' => event.agent_id,
-          'event_id' => event.id
-        }
+        GoogleCalendar.open(interpolate_options(options, event), Rails.logger) do |calendar|
+
+          cal_message = event.payload["message"]
+          if cal_message["start"].present? && cal_message["start"]["dateTime"].present? && !cal_message["start"]["date_time"].present?
+            cal_message["start"]["date_time"] = cal_message["start"].delete "dateTime"
+          end
+          if cal_message["end"].present? && cal_message["end"]["dateTime"].present? && !cal_message["end"]["date_time"].present?
+            cal_message["end"]["date_time"] = cal_message["end"].delete "dateTime"
+          end
+
+          calendar_event = calendar.publish_as(
+                interpolated(event)['calendar_id'],
+                cal_message
+              )
+
+          create_event :payload => {
+            'success' => true,
+            'published_calendar_event' => calendar_event,
+            'agent_id' => event.agent_id,
+            'event_id' => event.id
+          }
+        end
       end
     end
   end

+ 19 - 11
app/models/agents/google_translation_agent.rb

@@ -2,7 +2,7 @@ module Agents
   class GoogleTranslationAgent < Agent
     cannot_be_scheduled!
 
-    gem_dependency_check { defined?(Google) && defined?(Google::APIClient) }
+    gem_dependency_check { defined?(Google) && defined?(Google::Cloud::Translate) }
 
     description <<-MD
       The Translation Agent will attempt to translate text between natural languages.
@@ -12,6 +12,8 @@ module Agents
       Services are provided using Google Translate. You can [sign up](https://cloud.google.com/translate/) to get `google_api_key` which is required to use this agent.
       The service is **not free**.
 
+      To use credentials for the `google_api_key` use the liquid `credential` tag like so `{% credential google-api-key %}`
+
       `to` must be filled with a [translator language code](https://cloud.google.com/translate/docs/languages).
 
       `from` is the language translated from. If it's not specified, the API will attempt to detect the source language automatically and return it within the response.
@@ -56,7 +58,7 @@ module Agents
         opts = interpolated(event)
         opts['content'].each_pair do |key, value|
           result = translate(value)
-          translated_event[key] = result.data.translations.last.translated_text
+          translated_event[key] = result.text
         end
         create_event payload: translated_event
       end
@@ -77,16 +79,22 @@ module Agents
       @translate_service ||= google_client.discovered_api('translate','v2')
     end
 
+    def cloud_translate_service
+      # https://github.com/GoogleCloudPlatform/google-cloud-ruby/blob/master/google-cloud-translate/lib/google-cloud-translate.rb#L130
+      @google_client ||= Google::Cloud::Translate.new(key: interpolated['google_api_key'])
+    end
+
     def translate(value)
-      google_client.execute(
-        api_method: translate_service.translations.list,
-        parameters: {
-          format: 'text',
-          source: translate_from,
-          target: options["to"],
-          q: value
-        }
-      )
+      # google_client.execute(
+      #   api_method: translate_service.translations.list,
+      #   parameters: {
+      #     format: 'text',
+      #     source: translate_from,
+      #     target: options["to"],
+      #     q: value
+      #   }
+      # )
+      cloud_translate_service.translate(value, to: interpolated["to"], from: translate_from, format: "text")
     end
   end
 end

+ 21 - 0
app/views/application/_upgrade_warning.html.erb

@@ -24,6 +24,27 @@ TWITTER_OAUTH_SECRET=<%= @twitter_oauth_secret %>
     <% end %>
   </div>
 <% end -%>
+<% if @outdated_google_cal_agents && @outdated_google_cal_agents.length > 0 %>
+  <dir class="container">
+    <div class="alert alert-danger" role="alert">
+      <p>
+        <b>Warning!</b> Your <b>Google Calendar Publish Agents</b> are using an outdated version of Google Auth. You need to update the agent configuration, possibly with a new keyfile from Google for it to continue to work.
+      <br/>
+        PKCS12 key files (`.p12`) are no longer supported. Instead use RSA private keys. Edit the agents to see the updated instructions. The `google.key_secret` option is no longer needed, please remove it.
+      <br/>
+        The format of parameters in events passed to the agent have also changed. Parameters now are expected in underscore_case instead of camelCase (to match Ruby convention). Please update upstream agents that send events to the calendar agent.
+      </p>
+      <p>
+        Outdated agents:
+        <ul>
+        <% @outdated_google_cal_agents.each do |agent| -%>
+          <li><%= link_to(agent.name, edit_agent_path(agent)) %></li>
+        <% end -%>
+        </ul>
+      </p>
+    </div>
+  </dir>
+<% end -%>
 <% if @outdated_docker_image_namespace %>
   <dir class="container">
     <div class="alert alert-danger" role="alert">

+ 45 - 29
lib/google_calendar.rb

@@ -1,33 +1,44 @@
+require 'googleauth'
+require 'google/apis/calendar_v3'
+
 class GoogleCalendar
   def initialize(config, logger)
     @config = config
 
     if @config['google']['key'].present?
-      @key = OpenSSL::PKCS12.new(@config['google']['key'], @config['google']['key_secret']).key
-    else
-      @key = Google::APIClient::PKCS12.load_key(@config['google']['key_file'], @config['google']['key_secret'])
+      # https://github.com/google/google-auth-library-ruby/issues/65
+      # https://github.com/google/google-api-ruby-client/issues/370
+      ENV['GOOGLE_PRIVATE_KEY'] = @config['google']['key']
+      ENV['GOOGLE_CLIENT_EMAIL'] = @config['google']['service_account_email']
+      ENV['GOOGLE_ACCOUNT_TYPE'] = 'service_account'
+    elsif @config['google']['key_file'].present?
+      ENV['GOOGLE_APPLICATION_CREDENTIALS'] = @config['google']['key_file']
     end
 
-    @client = Google::APIClient.new(application_name: "Huginn", application_version: "0.0.1")
-    @client.retries = 2
     @logger ||= logger
 
-    @calendar = @client.discovered_api('calendar','v3')
+    # https://github.com/google/google-api-ruby-client/blob/master/MIGRATING.md
+    @calendar = Google::Apis::CalendarV3::CalendarService.new
+
+    # https://developers.google.com/api-client-library/ruby/auth/service-accounts
+    # https://developers.google.com/identity/protocols/application-default-credentials
+    scopes = [Google::Apis::CalendarV3::AUTH_CALENDAR]
+    @authorization = Google::Auth.get_application_default(scopes)
 
     @logger.info("Setup")
     @logger.debug @calendar.inspect
   end
 
+  def self.open(*args, &block)
+    instance = new(*args)
+    block.call(instance)
+  ensure
+    instance.cleanup!
+  end
+
   def auth_as
-    @client.authorization = Signet::OAuth2::Client.new({
-      token_credential_uri: 'https://accounts.google.com/o/oauth2/token',
-      audience:             'https://accounts.google.com/o/oauth2/token',
-      scope:                'https://www.googleapis.com/auth/calendar',
-      issuer:               @config['google']['service_account_email'],
-      signing_key:          @key
-    });
-
-    @client.authorization.fetch_access_token!
+    @authorization.fetch_access_token!
+    @calendar.authorization = @authorization
   end
 
   # who - String: email of user to add event
@@ -38,14 +49,15 @@ class GoogleCalendar
     @logger.info("Attempting to create event for " + who)
     @logger.debug details.to_yaml
 
-    ret = @client.execute(
-      api_method: @calendar.events.insert,
-      parameters: {'calendarId' => who, 'sendNotifications' => true},
-      body: details.to_json,
-      headers: {'Content-Type' => 'application/json'}
-    )
+    event = Google::Apis::CalendarV3::Event.new(details.deep_symbolize_keys)
+    ret = @calendar.insert_event(
+        who,
+        event,
+        send_notifications: true
+      )
+
     @logger.debug ret.to_yaml
-    ret
+    ret.to_h
   end
 
   def events_as(who, date)
@@ -56,14 +68,18 @@ class GoogleCalendar
     @logger.info("Attempting to receive events for "+who)
     @logger.debug details.to_yaml
 
-    ret = @client.execute(
-      api_method: @calendar.events.list,
-      parameters: {'calendarId' => who, 'sendNotifications' => true},
-      body: details.to_json,
-      headers: {'Content-Type' => 'application/json'}
-    )
+    ret = @calendar.list_events(
+        who
+      )
 
     @logger.debug ret.to_yaml
-    ret    
+    ret.to_h  
+  end
+
+  def cleanup!
+    ENV.delete('GOOGLE_PRIVATE_KEY')
+    ENV.delete('GOOGLE_CLIENT_EMAIL')
+    ENV.delete('GOOGLE_ACCOUNT_TYPE')
+    ENV.delete('GOOGLE_APPLICATION_CREDENTIALS')
   end
 end

+ 0 - 435
spec/cassettes/Agents_GoogleCalendarPublishAgent/_receive/should_publish_any_payload_it_receives.yml

@@ -1,435 +0,0 @@
----
-http_interactions:
-- request:
-    method: get
-    uri: https://www.googleapis.com/discovery/v1/apis/calendar/v3/rest
-    body:
-      encoding: UTF-8
-      string: ''
-    headers:
-      User-Agent:
-      - |-
-        Huginn/0.0.1 google-api-ruby-client/0.7.1 Linux/3.13.0-29-generic
-         (gzip)
-      Accept-Encoding:
-      - gzip
-      Content-Type:
-      - ''
-      Accept:
-      - "*/*"
-  response:
-    status:
-      code: 200
-      message: OK
-    headers:
-      Expires:
-      - Sat, 28 Jun 2014 17:21:12 GMT
-      Date:
-      - Sat, 28 Jun 2014 17:16:12 GMT
-      Etag:
-      - '"C11OM5Qtr9122-scy_WeqND9D3o/icy_kevyvyjgCKjN6s1gb_9TUZs"'
-      Content-Type:
-      - application/json; charset=UTF-8
-      Content-Encoding:
-      - gzip
-      X-Content-Type-Options:
-      - nosniff
-      X-Frame-Options:
-      - SAMEORIGIN
-      X-Xss-Protection:
-      - 1; mode=block
-      Content-Length:
-      - '11266'
-      Server:
-      - GSE
-      Age:
-      - '195'
-      Cache-Control:
-      - public, max-age=300, must-revalidate, no-transform
-      Alternate-Protocol:
-      - 443:quic
-    body:
-      encoding: ASCII-8BIT
-      string: !binary |-
-        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
-        4Rn8/2/P/j8y14+toGwBAA==
-    http_version: 
-  recorded_at: Sat, 28 Jun 2014 17:19:27 GMT
-- request:
-    method: post
-    uri: https://accounts.google.com/o/oauth2/token
-    body:
-      encoding: ASCII-8BIT
-      string: grant_type=urn%3Aietf%3Aparams%3Aoauth%3Agrant-type%3Ajwt-bearer&assertion=eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9.eyJpc3MiOiIxMDI5OTM2OTY2MzI2LW5jamQ3Nzc2cGNzcGM5OGhzZzgyZ3NiNTZ0MzIxN2VmQGRldmVsb3Blci5nc2VydmljZWFjY291bnQuY29tIiwic2NvcGUiOiJodHRwczovL3d3dy5nb29nbGVhcGlzLmNvbS9hdXRoL2NhbGVuZGFyIiwiYXVkIjoiaHR0cHM6Ly9hY2NvdW50cy5nb29nbGUuY29tL28vb2F1dGgyL3Rva2VuIiwiZXhwIjoxNDAzOTc2MDI3LCJpYXQiOjE0MDM5NzU5MDd9.G2t_IqQofzzXKJAySGu4MulAybOp3BjptAk_tra7-ALy2pWu0jKw8XblP4T0YPgyMcbhcSJ_OhYl7Inmkxc3xhWsN-ZQDtacIyLv9roIRbvm5zCFKceJRISu2nZuIUTGTeoVuotPh6KRRvXIk1RW_Rc7L0eLeGTWn1USqdNwlfU
-    headers:
-      Cache-Control:
-      - no-store
-      Content-Type:
-      - application/x-www-form-urlencoded
-      Accept-Encoding:
-      - gzip;q=1.0,deflate;q=0.6,identity;q=0.3
-      Accept:
-      - "*/*"
-      User-Agent:
-      - Ruby
-  response:
-    status:
-      code: 200
-      message: OK
-    headers:
-      Content-Type:
-      - application/json; charset=utf-8
-      Cache-Control:
-      - no-cache, no-store, max-age=0, must-revalidate
-      Pragma:
-      - no-cache
-      Expires:
-      - Fri, 01 Jan 1990 00:00:00 GMT
-      Date:
-      - Sat, 28 Jun 2014 17:19:28 GMT
-      Content-Disposition:
-      - attachment; filename="json.txt"; filename*=UTF-8''json.txt
-      X-Content-Type-Options:
-      - nosniff
-      X-Frame-Options:
-      - SAMEORIGIN
-      X-Xss-Protection:
-      - 1; mode=block
-      Server:
-      - GSE
-      Alternate-Protocol:
-      - 443:quic
-      Transfer-Encoding:
-      - chunked
-    body:
-      encoding: UTF-8
-      string: |-
-        {
-          "access_token" : "ya29.MgBhhNLHjzGOZBoAAADoyvaDO5G6GHmaxIYqa5EGZ5t8kL-unXKywIRYoYgQxw",
-          "token_type" : "Bearer",
-          "expires_in" : 3600
-        }
-    http_version: 
-  recorded_at: Sat, 28 Jun 2014 17:19:28 GMT
-- request:
-    method: post
-    uri: https://www.googleapis.com/calendar/v3/calendars/sqv39gj35tc837gdns1g4d81cg@group.calendar.google.com/events?sendNotifications=true
-    body:
-      encoding: UTF-8
-      string: '{"visibility":"default","summary":"Awesome event","description":"An
-        example event with text. Pro tip: DateTimes are in RFC3339","end":{"dateTime":"2014-10-02T11:00:00-05:00"},"start":{"dateTime":"2014-10-02T10:00:00-05:00"}}'
-    headers:
-      User-Agent:
-      - |-
-        Huginn/0.0.1 google-api-ruby-client/0.7.1 Linux/3.13.0-29-generic
-         (gzip)
-      Content-Type:
-      - application/json
-      Accept-Encoding:
-      - gzip
-      Authorization:
-      - Bearer ya29.MgBhhNLHjzGOZBoAAADoyvaDO5G6GHmaxIYqa5EGZ5t8kL-unXKywIRYoYgQxw
-      Cache-Control:
-      - no-store
-      Accept:
-      - "*/*"
-  response:
-    status:
-      code: 200
-      message: OK
-    headers:
-      Cache-Control:
-      - no-cache, no-store, max-age=0, must-revalidate
-      Pragma:
-      - no-cache
-      Expires:
-      - Fri, 01 Jan 1990 00:00:00 GMT
-      Date:
-      - Sat, 28 Jun 2014 17:19:31 GMT
-      Etag:
-      - '"BZUCgRsJHN1b3Y4VmmLXiJzEzGI/MjgwNzk1MTk0MTk3MjAwMA"'
-      Content-Type:
-      - application/json; charset=UTF-8
-      Content-Encoding:
-      - gzip
-      X-Content-Type-Options:
-      - nosniff
-      X-Frame-Options:
-      - SAMEORIGIN
-      X-Xss-Protection:
-      - 1; mode=block
-      Server:
-      - GSE
-      Alternate-Protocol:
-      - 443:quic
-      Transfer-Encoding:
-      - chunked
-    body:
-      encoding: ASCII-8BIT
-      string: !binary |-
-        H4sIAAAAAAAAAH1T0U7bMBR971dY3ePW1IlLmlSaRkdXRkWqCZUxKl6Mfeu6
-        iZ1gOw0E8e9LAh2btvFgKc49Pufce+zHHuqnUvP+BPUZzUBzat7BHrTrf2hK
-        4KhoSzf9z+vLE3FhF1+X/i25Hn1X6vyHXNRf6tOzYbIT1bJO/WSV4maRZDet
-        kulNv6OQHbfd850RocF7mpa3dp9FjJfW5zLtQNZRV9rORK430ijg3f+tU9m5
-        1Glb2TpX2MlwWFWVJ/JcZOCxXA0Proed608g+UdG1inVC73UZxVXc8OvFjVX
-        1eg6uPBZPU/plRWMzIOkzjTdJT4/TUZJzfX69KhOrhhez8T9dcCn66ozwQxQ
-        B10bAfZHAxwOgmjljyd+PAliD2O87nBlwf+LI9iLo/AZZ0ulqHlocdMKbK4A
-        vU6cg2VGFk7mugNoBPdUFdkLBlXSbZGDe+ehbyZHThYTNGtkV1KBRdQAkhpd
-        zE8IIfGr+9w0ZI891CSqqMxaZh8HcUzCOAxJEA402/HxeBwWzBYsjrZWRIGw
-        t0ehI4E/hs0xb+SzvADjCQtmLxlQxvJSuzaEfg89tVq5EVTLGv5Ws3d7Eosd
-        OXIsImPBtfXFiEc+E8fC5GXhHWL8LdrWfjMQaYuMPiypgpboaymk1ug8Z7Sd
-        UfMhnnEWsk0DcKaEFzfNpTLu4IS/zOhXOj4eYLLCuIlmgvF73GZ06AO6B/Hm
-        Of9f5+QJzS7PZm9f+OM/O2yM35WgWauA270B1TxIMPZgobQwgw0tM/faX++p
-        9xPxx2cruwMAAA==
-    http_version: 
-  recorded_at: Sat, 28 Jun 2014 17:19:31 GMT
-recorded_with: VCR 2.9.2

+ 126 - 227
spec/cassettes/Agents_GoogleTranslationAgent/_receive/checks_if_it_can_handle_multiple_events.yml

@@ -1,20 +1,22 @@
 ---
 http_interactions:
 - request:
-    method: get
-    uri: https://www.googleapis.com/discovery/v1/apis/translate/v2/rest?key=some_api_key
+    method: post
+    uri: https://translation.googleapis.com/language/translate/v2?key=some_api_key
     body:
       encoding: UTF-8
-      string: ''
+      string: '{"q":["hey what are you doing"],"target":"sv","source":"en","format":"text"}'
     headers:
       User-Agent:
-      - |-
-        Huginn/0.0.1 google-api-ruby-client/0.7.1 Mac OS X/10.11.6
-         (gzip)
-      Accept-Encoding:
-      - gzip
+      - gcloud-ruby/1.0.0
+      Google-Cloud-Resource-Prefix:
+      - projects/
       Content-Type:
-      - ''
+      - application/json
+      X-Goog-Api-Client:
+      - gl-ruby/2.3.4 gccl/1.0.0
+      Accept-Encoding:
+      - gzip;q=1.0,deflate;q=0.6,identity;q=0.3
       Accept:
       - "*/*"
   response:
@@ -22,154 +24,53 @@ http_interactions:
       code: 200
       message: OK
     headers:
-      Expires:
-      - Tue, 25 Apr 2017 10:49:56 GMT
-      Date:
-      - Tue, 25 Apr 2017 10:44:56 GMT
-      Etag:
-      - '"YWOzh2SDasdU84ArJnpYek-OMdg/6s__cFeA5l1i01rONlu3TmUQEHs"'
-      Vary:
-      - Origin
-      - X-Origin
       Content-Type:
       - application/json; charset=UTF-8
-      Content-Encoding:
-      - gzip
-      X-Content-Type-Options:
-      - nosniff
-      X-Frame-Options:
-      - SAMEORIGIN
-      X-Xss-Protection:
-      - 1; mode=block
-      Content-Length:
-      - '1870'
-      Server:
-      - GSE
-      Cache-Control:
-      - public, max-age=300, must-revalidate, no-transform
-      Age:
-      - '0'
-      Alt-Svc:
-      - quic=":443"; ma=2592000; v="37,36,35"
-    body:
-      encoding: ASCII-8BIT
-      string: !binary |-
-        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
-        ftEe3bRzPG9nVZiwDp4O/gfEQdY3CBwAAA==
-    http_version:
-  recorded_at: Tue, 25 Apr 2017 10:44:56 GMT
-- request:
-    method: get
-    uri: https://www.googleapis.com/language/translate/v2?format=text&key=some_api_key&q=hey%20what%20are%20you%20doing&source=en&target=sv
-    body:
-      encoding: UTF-8
-      string: ''
-    headers:
-      User-Agent:
-      - |-
-        Huginn/0.0.1 google-api-ruby-client/0.7.1 Mac OS X/10.11.6
-         (gzip)
-      Accept-Encoding:
-      - gzip
-      Content-Type:
-      - ''
-      Accept:
-      - "*/*"
-  response:
-    status:
-      code: 200
-      message: OK
-    headers:
-      Expires:
-      - Tue, 25 Apr 2017 10:44:56 GMT
-      Date:
-      - Tue, 25 Apr 2017 10:44:56 GMT
-      Cache-Control:
-      - private, max-age=0, must-revalidate, no-transform
-      Etag:
-      - '"0Etagq2wejMpn1NuD3Ltqh3qHnw/XGWNxMh7tDHIjrW_w-Ffgk19QCg"'
       Vary:
       - Origin
+      - Referer
       - X-Origin
-      Content-Type:
-      - application/json; charset=UTF-8
-      Content-Encoding:
-      - gzip
-      X-Content-Type-Options:
-      - nosniff
-      X-Frame-Options:
-      - SAMEORIGIN
+      Date:
+      - Fri, 30 Jun 2017 14:54:18 GMT
+      Server:
+      - ESF
+      Cache-Control:
+      - private
       X-Xss-Protection:
       - 1; mode=block
-      Server:
-      - GSE
+      X-Frame-Options:
+      - SAMEORIGIN
+      X-Content-Type-Options:
+      - nosniff
       Alt-Svc:
-      - quic=":443"; ma=2592000; v="37,36,35"
+      - quic=":443"; ma=2592000; v="39,38,37,36,35"
       Transfer-Encoding:
       - chunked
     body:
       encoding: ASCII-8BIT
       string: !binary |-
-        H4sIAAAAAAAAAKvmUlBKSSxJVLJSqOZSUFAqKUrMK85JLMnMzysGikUDxcAS
-        SFKpKSGpFSVASSWP1CyFssQUhfTD24oUUkqVQOpqgUQsF5Cq5QIA9kBPzVwA
-        AAA=
-    http_version:
-  recorded_at: Tue, 25 Apr 2017 10:44:56 GMT
+        ewogICJkYXRhIjogewogICAgInRyYW5zbGF0aW9ucyI6IFsKICAgICAgewog
+        ICAgICAgICJ0cmFuc2xhdGVkVGV4dCI6ICJIZWogdmFkIGfDtnIgZHUiCiAg
+        ICAgIH0KICAgIF0KICB9Cn0K
+    http_version: 
+  recorded_at: Fri, 30 Jun 2017 14:54:18 GMT
 - request:
-    method: get
-    uri: https://www.googleapis.com/language/translate/v2?format=text&key=some_api_key&q=do%20tell%20more&source=en&target=sv
+    method: post
+    uri: https://translation.googleapis.com/language/translate/v2?key=some_api_key
     body:
       encoding: UTF-8
-      string: ''
+      string: '{"q":["do tell more"],"target":"sv","source":"en","format":"text"}'
     headers:
       User-Agent:
-      - |-
-        Huginn/0.0.1 google-api-ruby-client/0.7.1 Mac OS X/10.11.6
-         (gzip)
-      Accept-Encoding:
-      - gzip
+      - gcloud-ruby/1.0.0
+      Google-Cloud-Resource-Prefix:
+      - projects/
       Content-Type:
-      - ''
+      - application/json
+      X-Goog-Api-Client:
+      - gl-ruby/2.3.4 gccl/1.0.0
+      Accept-Encoding:
+      - gzip;q=1.0,deflate;q=0.6,identity;q=0.3
       Accept:
       - "*/*"
   response:
@@ -177,55 +78,53 @@ http_interactions:
       code: 200
       message: OK
     headers:
-      Expires:
-      - Tue, 25 Apr 2017 10:44:56 GMT
-      Date:
-      - Tue, 25 Apr 2017 10:44:56 GMT
-      Cache-Control:
-      - private, max-age=0, must-revalidate, no-transform
-      Etag:
-      - '"0Etagq2wejMpn1NuD3Ltqh3qHnw/ojPXc7YpC7RgGxSvk9qvHc7p0bM"'
+      Content-Type:
+      - application/json; charset=UTF-8
       Vary:
       - Origin
+      - Referer
       - X-Origin
-      Content-Type:
-      - application/json; charset=UTF-8
-      Content-Encoding:
-      - gzip
-      X-Content-Type-Options:
-      - nosniff
-      X-Frame-Options:
-      - SAMEORIGIN
+      Date:
+      - Fri, 30 Jun 2017 14:54:19 GMT
+      Server:
+      - ESF
+      Cache-Control:
+      - private
       X-Xss-Protection:
       - 1; mode=block
-      Server:
-      - GSE
+      X-Frame-Options:
+      - SAMEORIGIN
+      X-Content-Type-Options:
+      - nosniff
       Alt-Svc:
-      - quic=":443"; ma=2592000; v="37,36,35"
+      - quic=":443"; ma=2592000; v="39,38,37,36,35"
       Transfer-Encoding:
       - chunked
     body:
       encoding: ASCII-8BIT
       string: !binary |-
-        H4sIAAAAAAAAAKvmUlBKSSxJVLJSqOZSUFAqKUrMK85JLMnMzysGikUDxcAS
-        SFKpKSGpFSVASaWk1KLDS0pKEhVyU4uUQIpqgUQsF5Cq5QIAoYw0jVkAAAA=
-    http_version:
-  recorded_at: Tue, 25 Apr 2017 10:44:56 GMT
+        ewogICJkYXRhIjogewogICAgInRyYW5zbGF0aW9ucyI6IFsKICAgICAgewog
+        ICAgICAgICJ0cmFuc2xhdGVkVGV4dCI6ICJCZXLDpHR0YSBtZXIiCiAgICAg
+        IH0KICAgIF0KICB9Cn0K
+    http_version: 
+  recorded_at: Fri, 30 Jun 2017 14:54:19 GMT
 - request:
-    method: get
-    uri: https://www.googleapis.com/language/translate/v2?format=text&key=some_api_key&q=value2&source=en&target=sv
+    method: post
+    uri: https://translation.googleapis.com/language/translate/v2?key=some_api_key
     body:
       encoding: UTF-8
-      string: ''
+      string: '{"q":["value2"],"target":"sv","source":"en","format":"text"}'
     headers:
       User-Agent:
-      - |-
-        Huginn/0.0.1 google-api-ruby-client/0.7.1 Mac OS X/10.11.6
-         (gzip)
-      Accept-Encoding:
-      - gzip
+      - gcloud-ruby/1.0.0
+      Google-Cloud-Resource-Prefix:
+      - projects/
       Content-Type:
-      - ''
+      - application/json
+      X-Goog-Api-Client:
+      - gl-ruby/2.3.4 gccl/1.0.0
+      Accept-Encoding:
+      - gzip;q=1.0,deflate;q=0.6,identity;q=0.3
       Accept:
       - "*/*"
   response:
@@ -233,55 +132,53 @@ http_interactions:
       code: 200
       message: OK
     headers:
-      Expires:
-      - Tue, 25 Apr 2017 10:44:56 GMT
-      Date:
-      - Tue, 25 Apr 2017 10:44:56 GMT
-      Cache-Control:
-      - private, max-age=0, must-revalidate, no-transform
-      Etag:
-      - '"0Etagq2wejMpn1NuD3Ltqh3qHnw/2iNW9phusbWGkf6jtxwOLwZpsUA"'
+      Content-Type:
+      - application/json; charset=UTF-8
       Vary:
       - Origin
+      - Referer
       - X-Origin
-      Content-Type:
-      - application/json; charset=UTF-8
-      Content-Encoding:
-      - gzip
-      X-Content-Type-Options:
-      - nosniff
-      X-Frame-Options:
-      - SAMEORIGIN
+      Date:
+      - Fri, 30 Jun 2017 14:54:19 GMT
+      Server:
+      - ESF
+      Cache-Control:
+      - private
       X-Xss-Protection:
       - 1; mode=block
-      Server:
-      - GSE
+      X-Frame-Options:
+      - SAMEORIGIN
+      X-Content-Type-Options:
+      - nosniff
       Alt-Svc:
-      - quic=":443"; ma=2592000; v="37,36,35"
+      - quic=":443"; ma=2592000; v="39,38,37,36,35"
       Transfer-Encoding:
       - chunked
     body:
       encoding: ASCII-8BIT
       string: !binary |-
-        H4sIAAAAAAAAAKvmUlBKSSxJVLJSqOZSUFAqKUrMK85JLMnMzysGikUDxcAS
-        SFKpKSGpFSVASaWyw0uKUlKNlEDytUAilgtI1XIBACGegeVUAAAA
-    http_version:
-  recorded_at: Tue, 25 Apr 2017 10:44:56 GMT
+        ewogICJkYXRhIjogewogICAgInRyYW5zbGF0aW9ucyI6IFsKICAgICAgewog
+        ICAgICAgICJ0cmFuc2xhdGVkVGV4dCI6ICJ2w6RyZGUyIgogICAgICB9CiAg
+        ICBdCiAgfQp9Cg==
+    http_version: 
+  recorded_at: Fri, 30 Jun 2017 14:54:19 GMT
 - request:
-    method: get
-    uri: https://www.googleapis.com/language/translate/v2?format=text&key=some_api_key&q=value1&source=en&target=sv
+    method: post
+    uri: https://translation.googleapis.com/language/translate/v2?key=some_api_key
     body:
       encoding: UTF-8
-      string: ''
+      string: '{"q":["value1"],"target":"sv","source":"en","format":"text"}'
     headers:
       User-Agent:
-      - |-
-        Huginn/0.0.1 google-api-ruby-client/0.7.1 Mac OS X/10.11.6
-         (gzip)
-      Accept-Encoding:
-      - gzip
+      - gcloud-ruby/1.0.0
+      Google-Cloud-Resource-Prefix:
+      - projects/
       Content-Type:
-      - ''
+      - application/json
+      X-Goog-Api-Client:
+      - gl-ruby/2.3.4 gccl/1.0.0
+      Accept-Encoding:
+      - gzip;q=1.0,deflate;q=0.6,identity;q=0.3
       Accept:
       - "*/*"
   response:
@@ -289,38 +186,40 @@ http_interactions:
       code: 200
       message: OK
     headers:
-      Expires:
-      - Tue, 25 Apr 2017 10:44:56 GMT
-      Date:
-      - Tue, 25 Apr 2017 10:44:56 GMT
-      Cache-Control:
-      - private, max-age=0, must-revalidate, no-transform
-      Etag:
-      - '"0Etagq2wejMpn1NuD3Ltqh3qHnw/nFM5Az6_LFcl8SfedeFzG_gVr5Y"'
+      Content-Type:
+      - application/json; charset=UTF-8
       Vary:
       - Origin
+      - Referer
       - X-Origin
-      Content-Type:
-      - application/json; charset=UTF-8
-      Content-Encoding:
-      - gzip
-      X-Content-Type-Options:
-      - nosniff
-      X-Frame-Options:
-      - SAMEORIGIN
+      Date:
+      - Fri, 30 Jun 2017 14:54:19 GMT
+      Server:
+      - ESF
+      Cache-Control:
+      - private
       X-Xss-Protection:
       - 1; mode=block
-      Server:
-      - GSE
+      X-Frame-Options:
+      - SAMEORIGIN
+      X-Content-Type-Options:
+      - nosniff
       Alt-Svc:
-      - quic=":443"; ma=2592000; v="37,36,35"
+      - quic=":443"; ma=2592000; v="39,38,37,36,35"
       Transfer-Encoding:
       - chunked
     body:
       encoding: ASCII-8BIT
-      string: !binary |-
-        H4sIAAAAAAAAAKvmUlBKSSxJVLJSqOZSUFAqKUrMK85JLMnMzysGikUDxcAS
-        SFKpKSGpFSVASaWyxJzSVEMlkHQtkIjlAlK1XAC9QY+aUwAAAA==
-    http_version:
-  recorded_at: Tue, 25 Apr 2017 10:44:56 GMT
-recorded_with: VCR 2.9.2
+      string: |
+        {
+          "data": {
+            "translations": [
+              {
+                "translatedText": "value1"
+              }
+            ]
+          }
+        }
+    http_version: 
+  recorded_at: Fri, 30 Jun 2017 14:54:19 GMT
+recorded_with: VCR 3.0.3

+ 107 - 23
spec/models/agents/google_calendar_publish_agent_spec.rb

@@ -1,6 +1,6 @@
 require 'rails_helper'
 
-describe Agents::GoogleCalendarPublishAgent, :vcr do
+describe Agents::GoogleCalendarPublishAgent do
   let(:valid_params) do
     {
       'expected_update_period_in_days' => "10",
@@ -21,34 +21,36 @@ describe Agents::GoogleCalendarPublishAgent, :vcr do
   end
 
   describe '#receive' do
-    let(:event) do
-      _event = Event.new
-      _event.agent = agents(:bob_manual_event_agent)
-      _event.payload = { 'message' => message }
-      _event.save!
-      _event
-    end
-
-    let(:calendar_id) { 'sqv39gj35tc837gdns1g4d81cg@group.calendar.google.com' }
     let(:message) do
       {
         'visibility' => 'default',
         'summary' => "Awesome event",
         'description' => "An example event with text. Pro tip: DateTimes are in RFC3339",
         'end' => {
-          'dateTime' => '2014-10-02T11:00:00-05:00'
+          'date_time' => '2014-10-02T11:00:00-05:00'
         },
         'start' => {
-          'dateTime' => '2014-10-02T10:00:00-05:00'
+          'date_time' => '2014-10-02T10:00:00-05:00'
         }
       }
     end
-    let(:response_body) do
+
+    let(:event) do
+      _event = Event.new
+      _event.agent = agents(:bob_manual_event_agent)
+      _event.payload = { 'message' => message }
+      _event.save!
+      _event
+    end
+
+    let(:calendar_id) { 'sqv39gj35tc837gdns1g4d81cg@group.calendar.google.com' }
+
+    let(:response_hash) do
       {"kind"=>"calendar#event",
         "etag"=>"\"2908684044040000\"",
         "id"=>"baz",
         "status"=>"confirmed",
-        "htmlLink"=>
+        "html_link"=>
           "https://calendar.google.com/calendar/event?eid=foobar",
         "created"=>"2016-02-01T15:53:41.000Z",
         "updated"=>"2016-02-01T15:53:42.020Z",
@@ -60,20 +62,21 @@ describe Agents::GoogleCalendarPublishAgent, :vcr do
             "blah-foobar@developer.gserviceaccount.com"},
         "organizer"=>
           {"email"=>calendar_id,
-            "displayName"=>"Huginn Location Log",
+            "display_name"=>"Huginn Location Log",
             "self"=>true},
-        "start"=>{"dateTime"=>"2014-10-03T00:30:00+09:30"},
-        "end"=>{"dateTime"=>"2014-10-03T01:30:00+09:30"},
-        "iCalUID"=>"blah@google.com",
+        "start"=>{"date_time"=>"2014-10-03T00:30:00+09:30"},
+        "end"=>{"date_time"=>"2014-10-03T01:30:00+09:30"},
+        "i_cal_uid"=>"blah@google.com",
         "sequence"=>0,
-        "reminders"=>{"useDefault"=>true}
-      }.to_json
+        "reminders"=>{"use_default"=>true}
+      }
     end
 
     def setup_mock!
       fake_interface = Object.new
       mock(GoogleCalendar).new(agent.interpolate_options(agent.options), Rails.logger) { fake_interface }
-      mock(fake_interface).publish_as(calendar_id, message) { stub!.response.stub!.body { response_body } }
+      mock(fake_interface).publish_as(calendar_id, message) { response_hash }
+      mock(fake_interface).cleanup!
     end
 
     describe 'when the calendar_id is in the options' do
@@ -84,7 +87,7 @@ describe Agents::GoogleCalendarPublishAgent, :vcr do
           agent.receive([event])
         }.to change { agent.events.count }.by(1)
 
-        expect(agent.events.last.payload).to eq({ "success" => true, "published_calendar_event" => JSON.parse(response_body), "agent_id" => event.agent_id, "event_id" => event.id })
+        expect(agent.events.last.payload).to eq({ "success" => true, "published_calendar_event" => response_hash, "agent_id" => event.agent_id, "event_id" => event.id })
       end
     end
 
@@ -101,7 +104,7 @@ describe Agents::GoogleCalendarPublishAgent, :vcr do
         agent.receive([event])
 
         expect(agent.events.count).to eq(1)
-        expect(agent.events.last.payload).to eq({ "success" => true, "published_calendar_event" => JSON.parse(response_body), "agent_id" => event.agent_id, "event_id" => event.id })
+        expect(agent.events.last.payload).to eq({ "success" => true, "published_calendar_event" => response_hash, "agent_id" => event.agent_id, "event_id" => event.id })
       end
 
       it 'should allow Liquid in the key' do
@@ -121,4 +124,85 @@ describe Agents::GoogleCalendarPublishAgent, :vcr do
       end
     end
   end
+
+  describe '#receive old style event' do
+    let(:event) do
+      _event = Event.new
+      _event.agent = agents(:bob_manual_event_agent)
+      _event.payload = { 'message' => {
+        'visibility' => 'default',
+        'summary' => "Awesome event",
+        'description' => "An example event with text. Pro tip: DateTimes are in RFC3339",
+        'end' => {
+          'dateTime' => '2014-10-02T11:00:00-05:00'
+        },
+        'start' => {
+          'dateTime' => '2014-10-02T10:00:00-05:00'
+        }
+      } }
+      _event.save!
+      _event
+    end
+
+    let(:calendar_id) { 'sqv39gj35tc837gdns1g4d81cg@group.calendar.google.com' }
+    let(:message) do
+      {
+        'visibility' => 'default',
+        'summary' => "Awesome event",
+        'description' => "An example event with text. Pro tip: DateTimes are in RFC3339",
+        'end' => {
+          'date_time' => '2014-10-02T11:00:00-05:00'
+        },
+        'start' => {
+          'date_time' => '2014-10-02T10:00:00-05:00'
+        }
+      }
+    end
+
+    let(:response_hash) do
+      {"kind"=>"calendar#event",
+        "etag"=>"\"2908684044040000\"",
+        "id"=>"baz",
+        "status"=>"confirmed",
+        "html_link"=>
+          "https://calendar.google.com/calendar/event?eid=foobar",
+        "created"=>"2016-02-01T15:53:41.000Z",
+        "updated"=>"2016-02-01T15:53:42.020Z",
+        "summary"=>"Awesome event",
+        "description"=>
+          "An example event with text. Pro tip: DateTimes are in RFC3339",
+        "creator"=>
+          {"email"=>
+            "blah-foobar@developer.gserviceaccount.com"},
+        "organizer"=>
+          {"email"=>calendar_id,
+            "display_name"=>"Huginn Location Log",
+            "self"=>true},
+        "start"=>{"date_time"=>"2014-10-03T00:30:00+09:30"},
+        "end"=>{"date_time"=>"2014-10-03T01:30:00+09:30"},
+        "i_cal_uid"=>"blah@google.com",
+        "sequence"=>0,
+        "reminders"=>{"use_default"=>true}
+      }
+    end
+
+    def setup_mock!
+      fake_interface = Object.new
+      mock(GoogleCalendar).new(agent.interpolate_options(agent.options), Rails.logger) { fake_interface }
+      mock(fake_interface).publish_as(calendar_id, message) { response_hash }
+      mock(fake_interface).cleanup!
+    end
+
+    describe 'when the calendar_id is in the options' do
+      it 'should publish old style payload it receives' do
+        setup_mock!
+
+        expect {
+          agent.receive([event])
+        }.to change { agent.events.count }.by(1)
+
+        expect(agent.events.last.payload).to eq({ "success" => true, "published_calendar_event" => response_hash, "agent_id" => event.agent_id, "event_id" => event.id })
+      end
+    end
+  end
 end