bps-google-api 0.0.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: '080247a8c27b19573d393857fec05b41d6cc86f4228839e8e95db37315cddd6b'
4
+ data.tar.gz: deebd40f8b6018230ff34fb637ce2041c9a65881254f54d33995dd0134dadf85
5
+ SHA512:
6
+ metadata.gz: 5a376bfde8ff5efcc98a2b397d452dd0f293db272445225b6411c650631bd0d2b05ecc7603f2086bc7803c7f417991beffd8b1359a0dac3c264510f641710337
7
+ data.tar.gz: 94edc78b009e1ac136178b3eefc1ccd4348aa7e5c34ad2b001350f13e9700af953daeb6545e763d1650547b2ceb0224c97b6c9aad587901b45bab85f12648b4d
data/Gemfile ADDED
@@ -0,0 +1,2 @@
1
+ source "https://rubygems.org"
2
+ gemspec
data/Gemfile.lock ADDED
@@ -0,0 +1,66 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ bps-google-api (0.0.5)
5
+ exp_retry (~> 0.0.8)
6
+ fileutils (~> 1.2)
7
+ google-api-client (~> 0.23.4)
8
+ ruby-progressbar (~> 1.10)
9
+
10
+ GEM
11
+ remote: https://rubygems.org/
12
+ specs:
13
+ addressable (2.6.0)
14
+ public_suffix (>= 2.0.2, < 4.0)
15
+ declarative (0.0.10)
16
+ declarative-option (0.1.0)
17
+ exp_retry (0.0.9)
18
+ faraday (0.15.4)
19
+ multipart-post (>= 1.2, < 3)
20
+ fileutils (1.2.0)
21
+ google-api-client (0.23.9)
22
+ addressable (~> 2.5, >= 2.5.1)
23
+ googleauth (>= 0.5, < 0.7.0)
24
+ httpclient (>= 2.8.1, < 3.0)
25
+ mime-types (~> 3.0)
26
+ representable (~> 3.0)
27
+ retriable (>= 2.0, < 4.0)
28
+ signet (~> 0.9)
29
+ googleauth (0.6.7)
30
+ faraday (~> 0.12)
31
+ jwt (>= 1.4, < 3.0)
32
+ memoist (~> 0.16)
33
+ multi_json (~> 1.11)
34
+ os (>= 0.9, < 2.0)
35
+ signet (~> 0.7)
36
+ httpclient (2.8.3)
37
+ jwt (2.1.0)
38
+ memoist (0.16.0)
39
+ mime-types (3.2.2)
40
+ mime-types-data (~> 3.2015)
41
+ mime-types-data (3.2019.0331)
42
+ multi_json (1.13.1)
43
+ multipart-post (2.0.0)
44
+ os (1.0.0)
45
+ public_suffix (3.0.3)
46
+ representable (3.0.4)
47
+ declarative (< 0.1.0)
48
+ declarative-option (< 0.2.0)
49
+ uber (< 0.2.0)
50
+ retriable (3.1.2)
51
+ ruby-progressbar (1.10.0)
52
+ signet (0.11.0)
53
+ addressable (~> 2.3)
54
+ faraday (~> 0.9)
55
+ jwt (>= 1.5, < 3.0)
56
+ multi_json (~> 1.10)
57
+ uber (0.1.0)
58
+
59
+ PLATFORMS
60
+ ruby
61
+
62
+ DEPENDENCIES
63
+ bps-google-api!
64
+
65
+ BUNDLED WITH
66
+ 2.0.0
data/Readme.md ADDED
@@ -0,0 +1,65 @@
1
+ # BPS Google API
2
+
3
+ A configured Google API wrapper.
4
+
5
+ ## Installation
6
+
7
+ Add this to your Gemfile:
8
+
9
+ ```ruby
10
+ gem 'bps-google-api'
11
+ ```
12
+
13
+ or install directly:
14
+
15
+ ```bash
16
+ gem install bps-google-api
17
+ ```
18
+
19
+ Then run the following in `config/application.rb`:
20
+
21
+ ```ruby
22
+ require 'google_api'
23
+ ```
24
+
25
+ ## Required Environment
26
+
27
+ ```bash
28
+ GOOGLE_CLIENT_ID
29
+ GOOGLE_CLIENT_SECRET
30
+
31
+ GOOGLE_ACCESS_TOKEN
32
+ GOOGLE_REFRESH_TOKEN
33
+ GOOGLE_AUTH_EXP
34
+
35
+ GOOGLE_AUTH_SCOPE_CALENDAR
36
+ GOOGLE_AUTH_SCOPE_GROUP
37
+
38
+ GOOGLE_CALENDAR_ID_GEN
39
+ GOOGLE_CALENDAR_ID_EDUC
40
+ GOOGLE_CALENDAR_ID_TEST
41
+ ```
42
+
43
+ ## Usage
44
+
45
+ ```ruby
46
+ calendar = GoogleAPI::Calendar.new
47
+
48
+ calendar.create(cal_id, event_options)
49
+ calendar.list(cal_id, max_results: 2500, page_token: nil)
50
+ calendar.get(cal_id, event_id)
51
+ calendar.update(cal_id, event_id, event_options)
52
+ calendar.delete(cal_id, event_id)
53
+
54
+ calendar.permit(calendar, user)
55
+ calendar.unpermit(calendar, user)
56
+ ```
57
+
58
+ ```ruby
59
+ group = GoogleAPI::Group.new('group@example.com')
60
+
61
+ group.get
62
+ group.members
63
+ group.add('somebody@example.com')
64
+ group.remove('somebody@example.com')
65
+ ```
@@ -0,0 +1,18 @@
1
+ Gem::Specification.new do |s|
2
+ s.name = 'bps-google-api'
3
+ s.version = '0.0.5'
4
+ s.date = '2019-04-11'
5
+ s.summary = 'Configured Google API'
6
+ s.description = 'A configured Google API wrapper.'
7
+ s.homepage = 'http://rubygems.org/gems/bps-google-api'
8
+ s.license = 'GPL-3.0'
9
+ s.authors = ['Julian Fiander']
10
+ s.email = 'julian@fiander.one'
11
+ s.require_paths = %w[lib]
12
+ s.files = `git ls-files`.split("\n")
13
+
14
+ s.add_runtime_dependency 'google-api-client', '~> 0.23.4'
15
+ s.add_runtime_dependency 'exp_retry', '~> 0.0.8'
16
+ s.add_runtime_dependency 'ruby-progressbar', '~> 1.10'
17
+ s.add_runtime_dependency 'fileutils', '~> 1.2'
18
+ end
data/lib/google_api.rb ADDED
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ module GoogleAPI
4
+ require 'google/apis/calendar_v3'
5
+ require 'google/apis/groupssettings_v1'
6
+ require 'google/apis/admin_directory_v1'
7
+ require 'googleauth'
8
+ require 'googleauth/stores/file_token_store'
9
+
10
+ require 'google_api/concerns'
11
+
12
+ require 'google_api/base'
13
+ require 'google_api/calendar'
14
+ require 'google_api/group'
15
+ end
@@ -0,0 +1,29 @@
1
+ # frozen_string_literal: true
2
+
3
+ module GoogleAPI
4
+ class Base
5
+ include GoogleAPI::Concerns::Base::Authorization
6
+
7
+ def initialize(auth: true)
8
+ self.authorize! if auth
9
+ end
10
+
11
+ def self.root_path
12
+ defined?(Rails) ? Rails.root : './'
13
+ end
14
+
15
+ private
16
+
17
+ def service
18
+ raise 'No service class defined.' unless defined?(service_class)
19
+
20
+ @service ||= service_class.new
21
+ end
22
+
23
+ def root_path
24
+ path = self.class.root_path
25
+ FileUtils.mkdir_p(path)
26
+ path
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,55 @@
1
+ # frozen_string_literal: true
2
+
3
+ module GoogleAPI
4
+ class Calendar < GoogleAPI::Base
5
+ include GoogleAPI::Concerns::Calendar::ClearTestCalendar
6
+ include GoogleAPI::Concerns::Calendar::Permission
7
+
8
+ LAST_TOKEN_PATH ||= "#{root_path}/tmp/run/last_page_token"
9
+
10
+ def create(calendar_id, event_options = {})
11
+ service.insert_event(calendar_id, event(event_options), conference_data_version: 1)
12
+ end
13
+
14
+ def list(calendar_id, max_results: 2500, page_token: nil)
15
+ service.list_events(calendar_id, max_results: max_results, page_token: page_token)
16
+ end
17
+
18
+ def get(calendar_id, event_id)
19
+ service.get_event(calendar_id, event_id)
20
+ end
21
+
22
+ def update(calendar_id, event_id, event_options = {})
23
+ service.update_event(calendar_id, event_id, event(event_options))
24
+ end
25
+
26
+ def delete(calendar_id, event_id)
27
+ service.delete_event(calendar_id, event_id)
28
+ rescue Google::Apis::ClientError
29
+ :event_not_found
30
+ end
31
+
32
+ private
33
+
34
+ def service_class
35
+ Google::Apis::CalendarV3::CalendarService
36
+ end
37
+
38
+ def event(event_options)
39
+ event_options.assert_valid_keys(valid_event_keys)
40
+ event_options[:start] = date(event_options[:start])
41
+ event_options[:end] = date(event_options[:end])
42
+
43
+ Google::Apis::CalendarV3::Event.new(event_options.reject { |_, v| v.nil? })
44
+ end
45
+
46
+ def valid_event_keys
47
+ %i[summary start end description location recurrence conference_data conference_data_version]
48
+ end
49
+
50
+ def date(date)
51
+ key = date&.is_a?(String) ? :date : :date_time
52
+ Google::Apis::CalendarV3::EventDateTime.new(key => date, time_zone: 'America/Detroit')
53
+ end
54
+ end
55
+ end
@@ -0,0 +1,14 @@
1
+ # frozen_string_literal: true
2
+
3
+ module GoogleAPI
4
+ module Concerns
5
+ module Base
6
+ require 'google_api/concerns/base/authorization'
7
+ end
8
+
9
+ module Calendar
10
+ require 'google_api/concerns/calendar/clear_test_calendar'
11
+ require 'google_api/concerns/calendar/permission'
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,110 @@
1
+ # frozen_string_literal: true
2
+
3
+ module GoogleAPI::Concerns::Base
4
+ module Authorization
5
+ OOB_URI ||= 'urn:ietf:wg:oauth:2.0:oob'
6
+ AUTH_SCOPES ||= [
7
+ Google::Apis::AdminDirectoryV1::AUTH_ADMIN_DIRECTORY_GROUP,
8
+ Google::Apis::CalendarV3::AUTH_CALENDAR
9
+ ].freeze
10
+
11
+ def authorize!(refresh: false, reveal: false)
12
+ client_id_file
13
+ token_file
14
+
15
+ auth = authorize(refresh: refresh)
16
+ service.authorization = auth
17
+ return true unless reveal
18
+
19
+ [auth, auth_keys(auth)]
20
+ end
21
+
22
+ private
23
+
24
+ def authorize(user_id = 'default', refresh: false)
25
+ @credentials = authorizer.get_credentials(user_id)
26
+ return @credentials unless @credentials.nil? || refresh
27
+
28
+ @credentials = authorizer.get_and_store_credentials_from_code(
29
+ user_id: user_id, code: request_authorization, base_url: OOB_URI
30
+ )
31
+ end
32
+
33
+ def request_authorization
34
+ url = authorizer.get_authorization_url(base_url: OOB_URI)
35
+ puts("Open this URL to authorize:\n", url)
36
+ print("\nResponse code: ")
37
+ gets
38
+ end
39
+
40
+ def authorizer
41
+ @authorizer ||= Google::Auth::UserAuthorizer.new(auth_client_id, AUTH_SCOPES, auth_token_store)
42
+ end
43
+
44
+ def auth_client_id
45
+ client_id_file
46
+ Google::Auth::ClientId.from_hash(JSON.parse(File.read('config/keys/google_api_client.json')))
47
+ end
48
+
49
+ def auth_token_store
50
+ Google::Auth::Stores::FileTokenStore.new(file: 'config/keys/google_token.yaml')
51
+ end
52
+
53
+ def auth_keys(auth)
54
+ {
55
+ GOOGLE_CLIENT_ID: auth.client_id, GOOGLE_CLIENT_SECRET: auth.client_secret,
56
+ GOOGLE_ACCESS_TOKEN: auth.access_token, GOOGLE_REFRESH_TOKEN: auth.refresh_token,
57
+ GOOGLE_AUTH_SCOPES: auth.scope, GOOGLE_AUTH_EXP: expires_milli(auth.as_json['expires_at'])
58
+ }
59
+ end
60
+
61
+ def expires_milli(time)
62
+ DateTime.strptime(time, '%Y-%m-%dT%H:%M:%S.%L%:z').to_i * 1000
63
+ end
64
+
65
+ def store_key(path, key)
66
+ return if File.exist?(path)
67
+
68
+ ensure_file(path)
69
+ File.open(path, 'w+') do |f|
70
+ File.chmod(0600, f)
71
+ block_given? ? yield(f) : f.write(key)
72
+ end
73
+ end
74
+
75
+ def ensure_file(path)
76
+ path = path.split('/')
77
+ file = path.pop
78
+ path = path.join('/')
79
+ FileUtils.mkdir_p(path)
80
+ end
81
+
82
+ def client_id_file
83
+ store_key(
84
+ File.join(root_path, 'config/keys/google_api_client.json'),
85
+ <<~KEY
86
+ {"installed":{"client_id":"#{ENV['GOOGLE_CLIENT_ID']}","project_id":"charming-scarab-208718",
87
+ "auth_uri":"https://accounts.google.com/o/oauth2/auth","token_uri":"https://accounts.google.com/o/oauth2/token",
88
+ "auth_provider_x509_cert_url":"https://www.googleapis.com/oauth2/v1/certs","client_secret":"#{ENV['GOOGLE_CLIENT_SECRET']}",
89
+ "redirect_uris":["#{OOB_URI}","http://localhost"]}}
90
+ KEY
91
+ )
92
+ end
93
+
94
+ def token_file
95
+ store_key(
96
+ File.join(root_path, 'config/keys/google_token.yaml'),
97
+ <<~KEY
98
+ ---
99
+ default: '{"client_id":"#{ENV['GOOGLE_CLIENT_ID']}","access_token":"#{ENV['GOOGLE_ACCESS_TOKEN']}",
100
+ "refresh_token":"#{ENV['GOOGLE_REFRESH_TOKEN']}","scope":[#{scopes}],
101
+ "expiration_time_millis":#{ENV['GOOGLE_AUTH_EXP']}}'
102
+ KEY
103
+ )
104
+ end
105
+
106
+ def scopes
107
+ ENV.select { |k, v| k =~ /GOOGLE_AUTH_SCOPE_/ }.values.map { |s| "\"#{s}\"" }.join(',')
108
+ end
109
+ end
110
+ end
@@ -0,0 +1,69 @@
1
+ # frozen_string_literal: true
2
+
3
+ module GoogleAPI::Concerns::Calendar
4
+ module ClearTestCalendar
5
+ RETRIABLE_EXCEPTIONS = [
6
+ Google::Apis::RateLimitError,
7
+ Google::Apis::TransmissionError
8
+ ].freeze
9
+
10
+ def clear_test_calendar(page_token: nil, page_limit: 50)
11
+ Google::Apis.logger.level = Logger::WARN
12
+ set_page_token(page_token)
13
+ loop_over_pages(ENV['GOOGLE_CALENDAR_ID_TEST'], page_limit: page_limit)
14
+ puts '*** Cleared all events!'
15
+ rescue Google::Apis::RateLimitError
16
+ puts "\n\n*** Google::Apis::RateLimitError (Rate Limit Exceeded)"
17
+ ensure
18
+ log_last_page_token unless @page_token.blank?
19
+ end
20
+
21
+ private
22
+
23
+ def set_page_token(page_token)
24
+ last_token = GoogleAPI::Calendar::LAST_TOKEN_PATH
25
+ @page_token ||= File.read(last_token) if File.exist?(last_token)
26
+ @page_token = page_token if page_token.present?
27
+ end
28
+
29
+ def loop_over_pages(cal_id, page_limit: 50)
30
+ puts "*** Starting with page token: #{@page_token}" if @page_token.present?
31
+
32
+ while (@page_token = clear_page(cal_id)) && page_limit.positive?
33
+ page_limit -= 1
34
+ end
35
+ end
36
+
37
+ def clear_page(cal_id)
38
+ response = list(cal_id, page_token: @page_token)
39
+ clear_events_from_page(cal_id, response.items) unless response.items.blank?
40
+ response.next_page_token
41
+ end
42
+
43
+ def clear_events_from_page(cal_id, items)
44
+ puts "*** Page token: #{@page_token}"
45
+ pb = progress_bar(items.count)
46
+ items&.each_with_index do |event, index|
47
+ ExpRetry.for(exception: RETRIABLE_EXCEPTIONS) do
48
+ delete(cal_id, event.id)
49
+ pb.increment
50
+ end
51
+ end
52
+ end
53
+
54
+ def log_last_page_token
55
+ puts "\n\n*** Last page token cleared: #{@page_token}"
56
+ File.open(GoogleAPI::Calendar::LAST_TOKEN_PATH, 'w+') do |f|
57
+ f.write(@page_token)
58
+ end
59
+ puts "\n*** Token stored in #{GoogleAPI::Calendar::LAST_TOKEN_PATH}"
60
+ end
61
+
62
+ def progress_bar(total)
63
+ ProgressBar.create(
64
+ title: 'Page cleared', starting_at: 0, total: total, progress_mark: ' ',
65
+ remainder_mark: "\u{FF65}", format: "%a [%R/sec] %E | %b\u{15E7}%i %c/%C (%P%%) %t"
66
+ )
67
+ end
68
+ end
69
+ end
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ module GoogleAPI::Concerns::Calendar
4
+ module Permission
5
+ def permit(calendar, user)
6
+ rule = Google::Apis::CalendarV3::AclRule.new(
7
+ scope: { type: 'user', value: user.email }, role: 'writer'
8
+ )
9
+
10
+ result = service.insert_acl(calendar, rule)
11
+ user.update(calendar_rule_id: result.id)
12
+ end
13
+
14
+ def unpermit(calendar, user)
15
+ service.delete_acl(calendar, user&.calendar_rule_id)
16
+ rescue Google::Apis::ClientError
17
+ :permission_not_found
18
+ ensure
19
+ user.update(calendar_rule_id: nil)
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,40 @@
1
+ # frozen_string_literal: true
2
+
3
+ module GoogleAPI
4
+ class Group < GoogleAPI::Base
5
+ def initialize(id, auth: false)
6
+ @group_id = id
7
+ super(auth: auth)
8
+ end
9
+
10
+ def get
11
+ service.get_group(@group_id)
12
+ end
13
+
14
+ def members
15
+ service.list_members(@group_id)
16
+ end
17
+
18
+ def add(email)
19
+ service.insert_member(@group_id, member(email))
20
+ rescue Google::Apis::ClientError, 'duplicate: Member already exists.'
21
+ :already_exists
22
+ end
23
+
24
+ def remove(email)
25
+ service.delete_member(@group_id, email)
26
+ rescue Google::Apis::ClientError, '(required: Missing required field: memberKey)'
27
+ :not_found
28
+ end
29
+
30
+ private
31
+
32
+ def service_class
33
+ Google::Apis::AdminDirectoryV1::DirectoryService
34
+ end
35
+
36
+ def member(email)
37
+ Google::Apis::AdminDirectoryV1::Member.new(email: email)
38
+ end
39
+ end
40
+ end
metadata ADDED
@@ -0,0 +1,110 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: bps-google-api
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.5
5
+ platform: ruby
6
+ authors:
7
+ - Julian Fiander
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2019-04-11 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: google-api-client
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: 0.23.4
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: 0.23.4
27
+ - !ruby/object:Gem::Dependency
28
+ name: exp_retry
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: 0.0.8
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: 0.0.8
41
+ - !ruby/object:Gem::Dependency
42
+ name: ruby-progressbar
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '1.10'
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '1.10'
55
+ - !ruby/object:Gem::Dependency
56
+ name: fileutils
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '1.2'
62
+ type: :runtime
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: '1.2'
69
+ description: A configured Google API wrapper.
70
+ email: julian@fiander.one
71
+ executables: []
72
+ extensions: []
73
+ extra_rdoc_files: []
74
+ files:
75
+ - Gemfile
76
+ - Gemfile.lock
77
+ - Readme.md
78
+ - bps-google-api.gemspec
79
+ - lib/google_api.rb
80
+ - lib/google_api/base.rb
81
+ - lib/google_api/calendar.rb
82
+ - lib/google_api/concerns.rb
83
+ - lib/google_api/concerns/base/authorization.rb
84
+ - lib/google_api/concerns/calendar/clear_test_calendar.rb
85
+ - lib/google_api/concerns/calendar/permission.rb
86
+ - lib/google_api/group.rb
87
+ homepage: http://rubygems.org/gems/bps-google-api
88
+ licenses:
89
+ - GPL-3.0
90
+ metadata: {}
91
+ post_install_message:
92
+ rdoc_options: []
93
+ require_paths:
94
+ - lib
95
+ required_ruby_version: !ruby/object:Gem::Requirement
96
+ requirements:
97
+ - - ">="
98
+ - !ruby/object:Gem::Version
99
+ version: '0'
100
+ required_rubygems_version: !ruby/object:Gem::Requirement
101
+ requirements:
102
+ - - ">="
103
+ - !ruby/object:Gem::Version
104
+ version: '0'
105
+ requirements: []
106
+ rubygems_version: 3.0.2
107
+ signing_key:
108
+ specification_version: 4
109
+ summary: Configured Google API
110
+ test_files: []