bps-google-api 0.0.5

Sign up to get free protection for your applications and to get access to all the features.
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: []