gcal_mapper 0.1.1
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +32 -0
- data/.travis.yml +16 -0
- data/CHANGELOG.md +15 -0
- data/Gemfile +20 -0
- data/Guardfile +19 -0
- data/LICENSE +22 -0
- data/README.md +370 -0
- data/Rakefile +32 -0
- data/bin/ci/file/auth.yaml +7 -0
- data/bin/ci/file/bad_yaml.yaml +0 -0
- data/bin/ci/file/config.yaml +8 -0
- data/bin/ci/file/privatekey.p12 +0 -0
- data/bin/ci/travis_build.sh +4 -0
- data/bin/ci/vcr/GcalMapper_Authentification/access_token_should_exist_with_assertion_auth.yml +49 -0
- data/bin/ci/vcr/GcalMapper_Authentification/should_be_fasle_if_bad_client_email_is_given.yml +47 -0
- data/bin/ci/vcr/GcalMapper_Authentification_Assertion/should_have_access_token_attribute.yml +49 -0
- data/bin/ci/vcr/GcalMapper_Authentification_Assertion/should_have_refresh_token_attribute.yml +49 -0
- data/bin/ci/vcr/GcalMapper_Calendar/should_get_the_calendar_list.yml +190 -0
- data/bin/ci/vcr/GcalMapper_Calendar/should_get_the_events_list.yml +45 -0
- data/bin/ci/vcr/GcalMapper_Calendar/should_raise_error_if_the_calendar_id_isn_t_accessible.yml +56 -0
- data/bin/ci/vcr/GcalMapper_Calendar/should_raise_error_if_the_token_is_bad.yml +60 -0
- data/bin/ci/vcr/GcalMapper_Mapper/should_save_all_events.yml +232 -0
- data/bin/ci/vcr/GcalMapper_Mapper/should_save_events_only_once.yml +232 -0
- data/bin/gcal-mapper +108 -0
- data/gcal_mapper.gemspec +25 -0
- data/lib/gcal_mapper.rb +15 -0
- data/lib/gcal_mapper/authentification.rb +57 -0
- data/lib/gcal_mapper/authentification/assertion.rb +110 -0
- data/lib/gcal_mapper/authentification/base.rb +20 -0
- data/lib/gcal_mapper/authentification/oauth2.rb +68 -0
- data/lib/gcal_mapper/calendar.rb +51 -0
- data/lib/gcal_mapper/configuration.rb +23 -0
- data/lib/gcal_mapper/errors.rb +40 -0
- data/lib/gcal_mapper/mapper.rb +76 -0
- data/lib/gcal_mapper/mapper/active_record.rb +74 -0
- data/lib/gcal_mapper/mapper/dsl.rb +57 -0
- data/lib/gcal_mapper/mapper/simple.rb +97 -0
- data/lib/gcal_mapper/railtie.rb +8 -0
- data/lib/gcal_mapper/rest_request.rb +73 -0
- data/lib/gcal_mapper/sync.rb +159 -0
- data/lib/gcal_mapper/version.rb +4 -0
- data/spec/authentification/assertion_spec.rb +15 -0
- data/spec/authentification/base_spec.rb +18 -0
- data/spec/authentification/oauth2_spec.rb +15 -0
- data/spec/authentification_spec.rb +34 -0
- data/spec/calendar_spec.rb +33 -0
- data/spec/gcal_mapper_spec.rb +5 -0
- data/spec/mapper/active_record_spec.rb +46 -0
- data/spec/mapper/dsl_spec.rb +31 -0
- data/spec/mapper/simple_spec.rb +48 -0
- data/spec/mapper_spec.rb +32 -0
- data/spec/rest_request_spec.rb +5 -0
- data/spec/spec_helper.rb +51 -0
- data/spec/support/models/event.rb +25 -0
- data/spec/support/models/event_jeu.rb +22 -0
- data/spec/support/schema.rb +28 -0
- data/spec/sync_spec.rb +28 -0
- metadata +200 -0
data/bin/gcal-mapper
ADDED
@@ -0,0 +1,108 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
bin_dir = File.expand_path("..", __FILE__)
|
3
|
+
lib_dir = File.expand_path("../lib", bin_dir)
|
4
|
+
|
5
|
+
$LOAD_PATH.unshift(lib_dir)
|
6
|
+
$LOAD_PATH.uniq!
|
7
|
+
|
8
|
+
require 'launchy'
|
9
|
+
require 'socket'
|
10
|
+
require 'gcal_mapper'
|
11
|
+
|
12
|
+
OAUTH_SERVER_PORT = 12736
|
13
|
+
|
14
|
+
options = {}
|
15
|
+
|
16
|
+
opt_parser = OptionParser.new do |opt|
|
17
|
+
|
18
|
+
opt.on('--client_id CLIENT_ID', 'your client id') do |client_id|
|
19
|
+
options[:client_id] = client_id
|
20
|
+
end
|
21
|
+
|
22
|
+
opt.on('--client_secret CLIENT_SECRET', 'your client secret') do |client_secret|
|
23
|
+
options[:client_secret] = client_secret
|
24
|
+
end
|
25
|
+
|
26
|
+
opt.on('-f FILE', '--file FILE', 'the file to save credentials') do |file|
|
27
|
+
options[:file] = file
|
28
|
+
end
|
29
|
+
|
30
|
+
opt.on('--scope SCOPE', 'the scope to ask authorization for') do |scope|
|
31
|
+
options[:scope] = scope
|
32
|
+
|
33
|
+
end
|
34
|
+
|
35
|
+
end
|
36
|
+
|
37
|
+
opt_parser.parse!
|
38
|
+
options[:scope] = 'https://www.googleapis.com/auth/calendar' if options[:scope].nil?
|
39
|
+
options[:file] = '~/google_auth.yaml' if options[:file].nil?
|
40
|
+
|
41
|
+
authorize_url = 'https://accounts.google.com/o/oauth2/auth?access_type=offline&approval_prompt=auto&'
|
42
|
+
authorize_url += 'client_id=' + options[:client_id] + '&'
|
43
|
+
authorize_url += 'redirect_uri=http://localhost:' + OAUTH_SERVER_PORT.to_s + '/&response_type=code&'
|
44
|
+
authorize_url += 'scope=' + options[:scope]
|
45
|
+
|
46
|
+
# open web browser with the authorisation page
|
47
|
+
Launchy.open(authorize_url)
|
48
|
+
|
49
|
+
msg = ''
|
50
|
+
|
51
|
+
# tcp server to receive the response and close the window
|
52
|
+
server = TCPServer.new('0.0.0.0', OAUTH_SERVER_PORT)
|
53
|
+
loop do
|
54
|
+
Thread.new(server.accept) do |client|
|
55
|
+
msg = client.readline
|
56
|
+
body = "<html><head><script>function closeWindow() {window.open('', '_self', '');window.close();} setTimeout(closeWindow, 10);</script></head><body>You may close this window.</body></html>\r\n"
|
57
|
+
headers = [
|
58
|
+
"",
|
59
|
+
"HTTP/1.1 200 OK",
|
60
|
+
"Date: Fri, 30 Sep 2011 08:11:27 GMT",
|
61
|
+
"Server: TCP socket test",
|
62
|
+
"Content-Type: text/html; charset=iso-8859-1",
|
63
|
+
"Content-Length: #{body.length}\r\n\r\n"].join("\r\n")
|
64
|
+
|
65
|
+
client.write headers
|
66
|
+
client.write body
|
67
|
+
|
68
|
+
client.close
|
69
|
+
end
|
70
|
+
|
71
|
+
sleep(1.0/24.0)
|
72
|
+
break if msg.include?('code')
|
73
|
+
end
|
74
|
+
|
75
|
+
# keep just the authorization code
|
76
|
+
msg.slice!('GET /?code=')
|
77
|
+
msg.chop!
|
78
|
+
msg.slice!(' HTTP/1.1')
|
79
|
+
|
80
|
+
# form rest request and send it
|
81
|
+
url = 'https://accounts.google.com/o/oauth2/token'
|
82
|
+
data = {
|
83
|
+
'code' => msg,
|
84
|
+
'grant_type'=> 'authorization_code',
|
85
|
+
'redirect_uri' => 'http://localhost:' + OAUTH_SERVER_PORT.to_s + '/',
|
86
|
+
'client_id' => options[:client_id],
|
87
|
+
'client_secret' => options[:client_secret]
|
88
|
+
}
|
89
|
+
opts = {
|
90
|
+
:method => :post,
|
91
|
+
:headers => {'Content-Type' => 'application/x-www-form-urlencoded'},
|
92
|
+
:parameters => data
|
93
|
+
}
|
94
|
+
req = GcalMapper::RestRequest.new(url, opts)
|
95
|
+
response = req.execute
|
96
|
+
|
97
|
+
# store credentials in yaml file
|
98
|
+
config = {
|
99
|
+
'mechanism' => 'oauth_2',
|
100
|
+
'scope' => options[:scope],
|
101
|
+
'client_id' => options[:client_id],
|
102
|
+
'client_secret' => options[:client_secret],
|
103
|
+
'access_token' => response['access_token'],
|
104
|
+
'refresh_token' => response['refresh_token']
|
105
|
+
}
|
106
|
+
config_file = File.expand_path(options[:file])
|
107
|
+
open(config_file, 'w') { |file| file.write(YAML.dump(config)) }
|
108
|
+
exit(0)
|
data/gcal_mapper.gemspec
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
require File.expand_path('../lib/gcal_mapper/version', __FILE__)
|
3
|
+
|
4
|
+
Gem::Specification.new do |gem|
|
5
|
+
gem.authors = ['Néville Dubuis']
|
6
|
+
gem.email = ['neville.dubuis@liquid-concept.ch']
|
7
|
+
gem.description = %q{A library to map Google Calendar events with an ORM}
|
8
|
+
gem.summary = %q{A library to map Google Calendar events with an ORM}
|
9
|
+
gem.homepage = 'http://rubygems.org/gems/gcal_mapper'
|
10
|
+
|
11
|
+
gem.files = `git ls-files`.split($\)
|
12
|
+
gem.executables = ['gcal-mapper']
|
13
|
+
gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
|
14
|
+
gem.name = 'gcal_mapper'
|
15
|
+
gem.require_paths = ['lib']
|
16
|
+
gem.version = GcalMapper::VERSION
|
17
|
+
|
18
|
+
gem.add_dependency 'launchy'
|
19
|
+
|
20
|
+
gem.add_development_dependency 'activerecord'
|
21
|
+
gem.add_development_dependency 'rspec', '>= 2.0'
|
22
|
+
gem.add_development_dependency 'vcr'
|
23
|
+
gem.add_development_dependency 'fakeweb'
|
24
|
+
|
25
|
+
end
|
data/lib/gcal_mapper.rb
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
require 'gcal_mapper/version'
|
2
|
+
require 'gcal_mapper/rest_request'
|
3
|
+
require 'gcal_mapper/authentification'
|
4
|
+
require 'gcal_mapper/calendar'
|
5
|
+
require 'gcal_mapper/errors'
|
6
|
+
require 'gcal_mapper/sync'
|
7
|
+
require 'gcal_mapper/mapper'
|
8
|
+
require 'gcal_mapper/configuration'
|
9
|
+
require 'gcal_mapper/railtie' if defined?(Rails)
|
10
|
+
|
11
|
+
#
|
12
|
+
# A library to map Google Calendar events with an ORM.
|
13
|
+
#
|
14
|
+
module GcalMapper
|
15
|
+
end
|
@@ -0,0 +1,57 @@
|
|
1
|
+
require 'gcal_mapper/authentification/assertion'
|
2
|
+
require 'gcal_mapper/authentification/oauth2'
|
3
|
+
|
4
|
+
module GcalMapper
|
5
|
+
#
|
6
|
+
# Abstract which type of authentification is required
|
7
|
+
#
|
8
|
+
class Authentification
|
9
|
+
REQUEST_URL = 'https://accounts.google.com/o/oauth2/token' # url where to request to authentificate
|
10
|
+
|
11
|
+
attr_reader :file # file that is needed for authentification
|
12
|
+
attr_reader :client_email # for assertion authentification
|
13
|
+
attr_reader :password # password for the p12 file
|
14
|
+
|
15
|
+
# intialize client info needed for connection to Oauth2.
|
16
|
+
#
|
17
|
+
# @param [String] file path to the yaml or p12 file
|
18
|
+
# @param [String] client_email email from the api_consol (service account only)
|
19
|
+
# @param [String] user_email email from the impersonated user (service account only)
|
20
|
+
# @param [String] password p12 key password (service account only)
|
21
|
+
def initialize(file, client_email=nil, user_email=nil, password='notasecret')
|
22
|
+
@file = File.expand_path(file)
|
23
|
+
@client_email = client_email
|
24
|
+
@user_email = user_email
|
25
|
+
@password = password
|
26
|
+
raise GcalMapper::AuthFileError if !File.exist?(@file)
|
27
|
+
end
|
28
|
+
|
29
|
+
# do the authentification for one of the right authentification method
|
30
|
+
#
|
31
|
+
# @return [Bool] true if instantiation ok
|
32
|
+
def authenticate
|
33
|
+
if client_email==nil
|
34
|
+
@auth = Authentification::Oauth2.new(@file)
|
35
|
+
else
|
36
|
+
@auth = Authentification::Assertion.new(@file, @client_email, @user_email, @password)
|
37
|
+
end
|
38
|
+
|
39
|
+
!access_token.nil?
|
40
|
+
end
|
41
|
+
|
42
|
+
# Gives the access token
|
43
|
+
#
|
44
|
+
# @return [string] the access token
|
45
|
+
def access_token
|
46
|
+
@auth.access_token
|
47
|
+
end
|
48
|
+
|
49
|
+
# refresh the access token
|
50
|
+
#
|
51
|
+
# @return [string] the access token
|
52
|
+
def refresh_token
|
53
|
+
@auth.refresh_token
|
54
|
+
end
|
55
|
+
|
56
|
+
end
|
57
|
+
end
|
@@ -0,0 +1,110 @@
|
|
1
|
+
require 'gcal_mapper/authentification/base'
|
2
|
+
require 'json'
|
3
|
+
|
4
|
+
module GcalMapper
|
5
|
+
class Authentification
|
6
|
+
#
|
7
|
+
# make the authentification for service account and request data from google calendar.
|
8
|
+
#
|
9
|
+
class Assertion < Authentification::Base
|
10
|
+
ASSERTION_SCOPE = 'https://www.google.com/calendar/feeds/' # scope for assertion
|
11
|
+
JWT_HEADER = {'alg' => 'RS256', 'typ' => 'JWT'} # header of the jwt to send
|
12
|
+
ASSERTION_TYPE = 'http://oauth.net/grant_type/jwt/1.0/bearer' # url to specify which type of assertion
|
13
|
+
|
14
|
+
|
15
|
+
attr_accessor :client_email # the email given by google for the service account
|
16
|
+
attr_accessor :user_email # the email of the user to impersonate
|
17
|
+
|
18
|
+
# New object
|
19
|
+
#
|
20
|
+
# @param [String] p12_file the path to the p12 key file
|
21
|
+
# @param [String] client_email email from the api_consol
|
22
|
+
# @param [String] user_email email from the impersonated user
|
23
|
+
# @param [String] password p12 key password
|
24
|
+
def initialize(p12_file, client_email, user_email, password)
|
25
|
+
@client_email = client_email
|
26
|
+
@p12_file = p12_file
|
27
|
+
@user_email = user_email
|
28
|
+
@password = password
|
29
|
+
request_token
|
30
|
+
@validity = Time.now.getutc.to_i
|
31
|
+
end
|
32
|
+
|
33
|
+
# give the acess token for th application and refresh if it is outdated
|
34
|
+
#
|
35
|
+
# @return [String] access token
|
36
|
+
def access_token
|
37
|
+
if Time.now.getutc.to_i - @validity > 3600
|
38
|
+
refresh_token
|
39
|
+
end
|
40
|
+
|
41
|
+
@access_token
|
42
|
+
end
|
43
|
+
|
44
|
+
# refresh the token by asking for a new one
|
45
|
+
#
|
46
|
+
# @return [String] access token
|
47
|
+
def refresh_token
|
48
|
+
request_token
|
49
|
+
end
|
50
|
+
|
51
|
+
private
|
52
|
+
# generate the JWT that must be include with the request acess token.
|
53
|
+
# the format of the JWT is (base64url encoded header).(base64url encoded claim set).(base64url encoded signature)
|
54
|
+
#
|
55
|
+
# @return [String] the generate JWT.
|
56
|
+
def generate_assertion()
|
57
|
+
encoded_header = [JWT_HEADER.to_json].pack("m0").tr("+/", "-_")
|
58
|
+
|
59
|
+
|
60
|
+
utc_time = Time.now.getutc.to_i
|
61
|
+
claim = {
|
62
|
+
'aud' => Authentification::REQUEST_URL,
|
63
|
+
'scope'=> ASSERTION_SCOPE,
|
64
|
+
'prn' => @user_email,
|
65
|
+
'iat' => utc_time,
|
66
|
+
'exp' => utc_time+3600,
|
67
|
+
'iss' => @client_email
|
68
|
+
}
|
69
|
+
encoded_claim = [claim.to_json].pack("m0").tr("+/", "-_")
|
70
|
+
begin
|
71
|
+
p12 = OpenSSL::PKCS12.new(File.read(@p12_file), @password)
|
72
|
+
rescue
|
73
|
+
raise GcalMapper::AuthFileError
|
74
|
+
end
|
75
|
+
key = p12.key
|
76
|
+
sign = key.sign(OpenSSL::Digest::SHA256.new, encoded_header + '.' + encoded_claim)
|
77
|
+
encoded_sign = [sign].pack("m0").tr("+/", "-_")
|
78
|
+
|
79
|
+
encoded_header + '.' + encoded_claim + '.' + encoded_sign
|
80
|
+
end
|
81
|
+
|
82
|
+
# Prepare all the parameters for sending the request token request to google
|
83
|
+
# and save the token in instance variable.
|
84
|
+
#
|
85
|
+
# @return [Hash] the HTTP response.
|
86
|
+
def request_token
|
87
|
+
data = {
|
88
|
+
'grant_type' => 'assertion',
|
89
|
+
'assertion_type' => ASSERTION_TYPE,
|
90
|
+
'assertion' => generate_assertion
|
91
|
+
}
|
92
|
+
options = {
|
93
|
+
:method => :post,
|
94
|
+
:headers => {'Content-Type' => 'application/x-www-form-urlencoded'},
|
95
|
+
:parameters => data
|
96
|
+
}
|
97
|
+
req = GcalMapper::RestRequest.new(Authentification::REQUEST_URL, options)
|
98
|
+
begin
|
99
|
+
response = req.execute
|
100
|
+
rescue
|
101
|
+
raise GcalMapper::AuthentificationError
|
102
|
+
end
|
103
|
+
@valididity = Time.now.getutc.to_i
|
104
|
+
|
105
|
+
@access_token = response['access_token']
|
106
|
+
end
|
107
|
+
|
108
|
+
end
|
109
|
+
end
|
110
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
module GcalMapper
|
2
|
+
class Authentification
|
3
|
+
#
|
4
|
+
# Base class for authentification methods
|
5
|
+
#
|
6
|
+
class Base
|
7
|
+
|
8
|
+
# raise error if this method is called -> mean that child class has not implemeted this method
|
9
|
+
def access_token
|
10
|
+
raise NotImplementedError
|
11
|
+
end
|
12
|
+
|
13
|
+
# raise error if this method is called -> mean that child class has not implemeted this method
|
14
|
+
def refresh_token
|
15
|
+
raise NotImplementedError
|
16
|
+
end
|
17
|
+
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,68 @@
|
|
1
|
+
require 'gcal_mapper/authentification/base'
|
2
|
+
require 'yaml'
|
3
|
+
|
4
|
+
module GcalMapper
|
5
|
+
class Authentification
|
6
|
+
#
|
7
|
+
# make the authentification with Oauth2 and request data from google calendar.
|
8
|
+
#
|
9
|
+
class Oauth2 < Authentification::Base
|
10
|
+
|
11
|
+
# intialize client info needed for connection to Oauth2.
|
12
|
+
#
|
13
|
+
# @param [String] yaml_file path to the yaml file which contains acess token, ...
|
14
|
+
def initialize (yaml_file)
|
15
|
+
begin
|
16
|
+
oauth_yaml = YAML.load_file(yaml_file)
|
17
|
+
@client_id = oauth_yaml["client_id"]
|
18
|
+
@client_secret = oauth_yaml["client_secret"]
|
19
|
+
@scope = oauth_yaml["scope"]
|
20
|
+
@refresh_token = oauth_yaml["refresh_token"]
|
21
|
+
@access_token = oauth_yaml["access_token"]
|
22
|
+
@validity = Time.now.getutc.to_i
|
23
|
+
rescue
|
24
|
+
raise GcalMapper::AuthFileError
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
# give the acess token for th application and refresh if it is outdated
|
29
|
+
#
|
30
|
+
# @return [String] access token
|
31
|
+
def access_token
|
32
|
+
if (Time.now.getutc.to_i - @validity) > 3600
|
33
|
+
refresh_token
|
34
|
+
end
|
35
|
+
|
36
|
+
@access_token
|
37
|
+
end
|
38
|
+
|
39
|
+
# refresh the token by using refresh token
|
40
|
+
#
|
41
|
+
# @return [String] access token
|
42
|
+
def refresh_token
|
43
|
+
data = {
|
44
|
+
'client_id' => @client_id,
|
45
|
+
'client_secret' => @client_secret,
|
46
|
+
'refresh_token' => @refresh_token,
|
47
|
+
'grant_type' => 'refresh_token'
|
48
|
+
}
|
49
|
+
options = {
|
50
|
+
:method => :post,
|
51
|
+
:headers => {'Content-Type' => 'application/x-www-form-urlencoded'},
|
52
|
+
:parameters => data
|
53
|
+
}
|
54
|
+
req = GcalMapper::RestRequest.new(Authentification::REQUEST_URL, options)
|
55
|
+
begin
|
56
|
+
response = req.execute
|
57
|
+
rescue
|
58
|
+
raise GcalMapper::AuthentificationError
|
59
|
+
end
|
60
|
+
@validity = Time.now.getutc.to_i
|
61
|
+
|
62
|
+
@access_token = response['access_token']
|
63
|
+
response
|
64
|
+
end
|
65
|
+
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
@@ -0,0 +1,51 @@
|
|
1
|
+
module GcalMapper
|
2
|
+
#
|
3
|
+
# Provide methods to get google calendar data
|
4
|
+
#
|
5
|
+
class Calendar
|
6
|
+
|
7
|
+
# Get the calendar list for the connected user.
|
8
|
+
#
|
9
|
+
# @param [Hash] access_token the token obtain with Authentification.access_token
|
10
|
+
# @return [Array] google calendar id that the user can access.
|
11
|
+
def get_calendars_list(access_token)
|
12
|
+
url = 'https://www.googleapis.com/calendar/v3/users/me/calendarList'
|
13
|
+
options = {
|
14
|
+
:method => :get,
|
15
|
+
:headers => {'Authorization' => 'Bearer ' + access_token}
|
16
|
+
}
|
17
|
+
req = GcalMapper::RestRequest.new(url, options)
|
18
|
+
response = req.execute
|
19
|
+
|
20
|
+
tab_cal=response['items']
|
21
|
+
ids = []
|
22
|
+
|
23
|
+
tab_cal.each do |t|
|
24
|
+
t.each do |k, v|
|
25
|
+
if k == 'id'
|
26
|
+
ids.push(v)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
ids
|
32
|
+
end
|
33
|
+
|
34
|
+
# Get all events from specified calendar(s).
|
35
|
+
#
|
36
|
+
# @param [Hash] access_token the token obtain with Authentification.access_token
|
37
|
+
# @param [Array] calendar_id contain the calendar(s) id you want to map
|
38
|
+
# @return [Array] all events from given calendar(s) id.
|
39
|
+
def get_events_list(access_token, calendar_id)
|
40
|
+
url = 'https://www.googleapis.com/calendar/v3/calendars/'+calendar_id+'/events?showDeleted=true'
|
41
|
+
options = {
|
42
|
+
:method => :get,
|
43
|
+
:headers => {'Authorization' => 'Bearer ' + access_token},
|
44
|
+
}
|
45
|
+
req = GcalMapper::RestRequest.new(url, options)
|
46
|
+
response = req.execute
|
47
|
+
response['items']
|
48
|
+
end
|
49
|
+
|
50
|
+
end
|
51
|
+
end
|