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
@@ -0,0 +1,159 @@
|
|
1
|
+
module GcalMapper
|
2
|
+
#
|
3
|
+
# Provide methods to synch google calendar and local bd
|
4
|
+
#
|
5
|
+
class Sync
|
6
|
+
|
7
|
+
# proxy to call sync methods and set new instance
|
8
|
+
#
|
9
|
+
# @param [Configuration] config configuration class seted by DSL
|
10
|
+
# @param [Class] base class of the caller
|
11
|
+
def self.sync(config, base)
|
12
|
+
new(config, base).sync_gcal
|
13
|
+
end
|
14
|
+
|
15
|
+
# new Sync object
|
16
|
+
#
|
17
|
+
# @param [Configuration] config configuration class seted by DSL
|
18
|
+
# @param [Class] base class of the calle
|
19
|
+
def initialize(config, base)
|
20
|
+
@config = config
|
21
|
+
@base = base
|
22
|
+
end
|
23
|
+
|
24
|
+
# full synchronization step by step
|
25
|
+
#
|
26
|
+
def sync_gcal
|
27
|
+
authenticate
|
28
|
+
fetch_events
|
29
|
+
save_events
|
30
|
+
end
|
31
|
+
|
32
|
+
private
|
33
|
+
# call Authentification class to authenticate the API
|
34
|
+
#
|
35
|
+
def authenticate
|
36
|
+
@auth = GcalMapper::Authentification.new(@config.file, @config.client_email, @config.user_email, @config.password)
|
37
|
+
@auth.authenticate
|
38
|
+
end
|
39
|
+
|
40
|
+
# Get all events from specified calendar(s).
|
41
|
+
#
|
42
|
+
# @return [Array] all events from given calendar(s) id.
|
43
|
+
def fetch_events
|
44
|
+
events_list = []
|
45
|
+
|
46
|
+
calendar = GcalMapper::Calendar.new
|
47
|
+
retried = false
|
48
|
+
begin
|
49
|
+
calendars_list = calendar.get_calendars_list(@auth.access_token)
|
50
|
+
rescue
|
51
|
+
if !retried
|
52
|
+
@auth.refresh_token
|
53
|
+
retried = true
|
54
|
+
retry
|
55
|
+
else
|
56
|
+
raise
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
if calendars_list.empty?
|
61
|
+
raise GcalMapper::CalendarAvailabilityError
|
62
|
+
end
|
63
|
+
|
64
|
+
@config.calendars.each do |cal|
|
65
|
+
events_list = []
|
66
|
+
if calendars_list.include?(cal)
|
67
|
+
retried = false
|
68
|
+
begin
|
69
|
+
events_list += (calendar.get_events_list(@auth.access_token, cal))
|
70
|
+
rescue
|
71
|
+
if !retried
|
72
|
+
@auth.refresh_token
|
73
|
+
retried = true
|
74
|
+
retry
|
75
|
+
else
|
76
|
+
raise
|
77
|
+
end
|
78
|
+
end
|
79
|
+
else
|
80
|
+
raise GcalMapper::CalendarAccessibilityError
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
@events_list = events_list
|
85
|
+
end
|
86
|
+
|
87
|
+
# Get all events from specified calendar(s). an keep synchronize with the online gcal
|
88
|
+
#
|
89
|
+
def save_events
|
90
|
+
adapter = Mapper.adapter
|
91
|
+
|
92
|
+
@events_list.each do |event|
|
93
|
+
|
94
|
+
existed_event = adapter.find_by(@config.gid, event['id'])
|
95
|
+
event_exist = !existed_event.nil?
|
96
|
+
|
97
|
+
if event_exist
|
98
|
+
if event['status'] == 'cancelled'
|
99
|
+
adapter.delete!(existed_event.id)
|
100
|
+
else
|
101
|
+
updated_attrib = set_attrib(event)
|
102
|
+
current_attrib = existed_event.attributes
|
103
|
+
current_attrib.delete('id')
|
104
|
+
if current_attrib != updated_attrib
|
105
|
+
adapter.update!(existed_event.id, updated_attrib)
|
106
|
+
end
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
if !event_exist && event['status'] != 'cancelled'
|
111
|
+
adapter.create!(set_attrib(event))
|
112
|
+
end
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
116
|
+
# set the object from fields configuration
|
117
|
+
#
|
118
|
+
# @param [Hash] event event to save in obj
|
119
|
+
# @return [hash] hash of setted attributes
|
120
|
+
def set_attrib(event)
|
121
|
+
attrib_hash = {@config.gid => event['id']}
|
122
|
+
@config.fields.each do |field, source|
|
123
|
+
if source[:source].include?('.')
|
124
|
+
data = source[:source].split('.')
|
125
|
+
attrib_hash[field] = eval_value(source, event[data[0]][data[1]])
|
126
|
+
else
|
127
|
+
attrib_hash[field] = eval_value(source, event[source[:source]])
|
128
|
+
end
|
129
|
+
if (attrib_hash[field].nil? && source.has_key?(:if_empty))
|
130
|
+
if source[:if_empty].include?('.')
|
131
|
+
data = source[:if_empty].split('.')
|
132
|
+
attrib_hash[field] = eval_value(source, event[data[0]][data[1]])
|
133
|
+
else
|
134
|
+
attrib_hash[field] = eval_value(source, event[source[:if_empty]])
|
135
|
+
end
|
136
|
+
end
|
137
|
+
end
|
138
|
+
|
139
|
+
attrib_hash
|
140
|
+
end
|
141
|
+
|
142
|
+
# eval the value from the source options
|
143
|
+
#
|
144
|
+
# @param [Hash] source source of the field
|
145
|
+
# @param [string] raw_data the string extract form google event data
|
146
|
+
# @return the data asked
|
147
|
+
def eval_value(source, raw_data)
|
148
|
+
if source[:match]
|
149
|
+
data = source[:match].match(raw_data).to_s
|
150
|
+
data = source[:default] if data == ''
|
151
|
+
else
|
152
|
+
data = raw_data
|
153
|
+
end
|
154
|
+
|
155
|
+
data
|
156
|
+
end
|
157
|
+
|
158
|
+
end
|
159
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe GcalMapper::Authentification::Assertion do
|
4
|
+
|
5
|
+
it "should have access_token attribute", :vcr do
|
6
|
+
auth = GcalMapper::Authentification::Assertion.new(@p12, @client_email, @user_email, 'notasecret')
|
7
|
+
auth.should respond_to(:access_token)
|
8
|
+
end
|
9
|
+
|
10
|
+
it "should have refresh_token attribute", :vcr do
|
11
|
+
auth = GcalMapper::Authentification::Assertion.new(@p12, @client_email, @user_email, 'notasecret')
|
12
|
+
auth.should respond_to(:refresh_token)
|
13
|
+
end
|
14
|
+
|
15
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe GcalMapper::Authentification::Base do
|
4
|
+
before :all do
|
5
|
+
class DummyClass < GcalMapper::Authentification::Base
|
6
|
+
end
|
7
|
+
|
8
|
+
@dummy = DummyClass.new
|
9
|
+
end
|
10
|
+
|
11
|
+
it "should raise error if access_token don't exist" do
|
12
|
+
expect {@dummy.access_token}.to raise_error(NotImplementedError)
|
13
|
+
end
|
14
|
+
|
15
|
+
it "should raise error if refresh_token don't exist" do
|
16
|
+
expect {@dummy.refresh_token}.to raise_error(NotImplementedError)
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe GcalMapper::Authentification::Oauth2 do
|
4
|
+
|
5
|
+
it "should have access_token attribute" do
|
6
|
+
auth = GcalMapper::Authentification::Oauth2.new(@yaml)
|
7
|
+
auth.should respond_to(:access_token)
|
8
|
+
end
|
9
|
+
|
10
|
+
it "should have refresh_token attribute", :vcr do
|
11
|
+
auth = GcalMapper::Authentification::Oauth2.new(@yaml)
|
12
|
+
auth.should respond_to(:refresh_token)
|
13
|
+
end
|
14
|
+
|
15
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe GcalMapper::Authentification do
|
4
|
+
|
5
|
+
it "should raise an error if the file is don't exist" do
|
6
|
+
expect {GcalMapper::Authentification.new('/home/test')}.to raise_error
|
7
|
+
end
|
8
|
+
|
9
|
+
it "should return an error if bad yaml file is given" do
|
10
|
+
auth = GcalMapper::Authentification.new(@bad_yaml)
|
11
|
+
expect {auth.authenticate}.to raise_error
|
12
|
+
end
|
13
|
+
|
14
|
+
it "should not raise an error if relative path is given" do
|
15
|
+
auth = GcalMapper::Authentification.new(@yaml_relative)
|
16
|
+
expect {auth.authenticate}.to_not raise_error
|
17
|
+
end
|
18
|
+
|
19
|
+
it "access token should exist with oauth2 auth" do
|
20
|
+
auth = GcalMapper::Authentification.new(@yaml)
|
21
|
+
auth.authenticate.should be_true
|
22
|
+
end
|
23
|
+
|
24
|
+
it "access token should exist with assertion auth", :vcr do
|
25
|
+
auth = GcalMapper::Authentification.new(@p12, @client_email)
|
26
|
+
auth.authenticate.should be_true
|
27
|
+
end
|
28
|
+
|
29
|
+
it "should be fasle if bad client email is given", :vcr do
|
30
|
+
auth = GcalMapper::Authentification.new(@p12, 'test@dtest.com')
|
31
|
+
expect {auth.authenticate}.to raise_error
|
32
|
+
end
|
33
|
+
|
34
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe GcalMapper::Calendar do
|
4
|
+
before :all do
|
5
|
+
@auth_oauth2 = GcalMapper::Authentification.new(@yaml)
|
6
|
+
@auth_oauth2.authenticate
|
7
|
+
@list = GcalMapper::Calendar.new
|
8
|
+
@bad_token = 'a bad token'
|
9
|
+
|
10
|
+
end
|
11
|
+
|
12
|
+
it "should get the calendar list", :vcr do
|
13
|
+
begin
|
14
|
+
calendar = @list.get_calendars_list(@auth_oauth2.access_token)
|
15
|
+
rescue
|
16
|
+
@auth_oauth2.refresh_token
|
17
|
+
calendar = @list.get_calendars_list(@auth_oauth2.access_token)
|
18
|
+
end
|
19
|
+
@list.get_calendars_list(@auth_oauth2.access_token).should_not be_nil
|
20
|
+
end
|
21
|
+
|
22
|
+
it "should get the events list", :vcr do
|
23
|
+
@list.get_events_list(@auth_oauth2.access_token, @calendar_id).should_not be_nil
|
24
|
+
end
|
25
|
+
|
26
|
+
it "should raise error if the token is bad", :vcr do
|
27
|
+
expect {@list.get_calendars_list(@bad_token)}.to raise_error
|
28
|
+
end
|
29
|
+
|
30
|
+
it "should raise error if the calendar id isn't accessible", :vcr do
|
31
|
+
expect {@list.get_events_list(@auth_oauth2.access_token, 'test')}.to raise_error
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe GcalMapper::Mapper::ActiveRecord do
|
4
|
+
|
5
|
+
before :all do
|
6
|
+
class User < ActiveRecord::Base
|
7
|
+
|
8
|
+
end
|
9
|
+
|
10
|
+
@klass = GcalMapper::Mapper::ActiveRecord::ActiveRecord.new(User)
|
11
|
+
end
|
12
|
+
|
13
|
+
it "should create a new object and save it" do
|
14
|
+
@klass.create!({'first_name' => 'a_name', 'name' => 'a_name'})
|
15
|
+
User.all.should_not eql([])
|
16
|
+
end
|
17
|
+
|
18
|
+
it "shoudl list all stored entry" do
|
19
|
+
@klass.find_all.should eql(User.all)
|
20
|
+
end
|
21
|
+
|
22
|
+
it "should update an entry" do
|
23
|
+
user = User.first
|
24
|
+
name_before = user.name
|
25
|
+
@klass.update!(user.id, {'name' => 'another_name'})
|
26
|
+
name_after = User.first.name
|
27
|
+
|
28
|
+
name_before.should_not eql(name_after)
|
29
|
+
end
|
30
|
+
|
31
|
+
it "should delete an entry" do
|
32
|
+
user = User.first
|
33
|
+
@klass.delete!(user.id)
|
34
|
+
|
35
|
+
User.all.should eql([])
|
36
|
+
end
|
37
|
+
|
38
|
+
it "should find an entry from field name and value" do
|
39
|
+
@klass.create!({'first_name' => 'a_name', 'name' => 'a_name'})
|
40
|
+
@klass.create!({'first_name' => 'a_name', 'name' => 'another_name'})
|
41
|
+
@klass.create!({'first_name' => 'a_name', 'name' => 'different_name'})
|
42
|
+
|
43
|
+
@klass.find_by('name', 'a_name').should be_an_instance_of(User)
|
44
|
+
end
|
45
|
+
|
46
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe GcalMapper::Mapper::DSL do
|
4
|
+
before :all do
|
5
|
+
@dsl = GcalMapper::Mapper::DSL.new(GcalMapper::Configuration.new)
|
6
|
+
end
|
7
|
+
|
8
|
+
describe :field do
|
9
|
+
it "should raise error if field option don't exist" do
|
10
|
+
expect {@dsl.field('test', {:source => 'test', :test => 'test'})}.to raise_error
|
11
|
+
end
|
12
|
+
|
13
|
+
it "should accept :source, :match, :default" do
|
14
|
+
expect {@dsl.field('test', {:source => 'test', :match => /test/, :default => 'test'})}.to_not raise_error
|
15
|
+
end
|
16
|
+
|
17
|
+
it "should raise error if regexp is invalid" do
|
18
|
+
expect {@dsl.field('test', {:source => 'test', :match => test, :default => 'test'})}.to raise_error
|
19
|
+
end
|
20
|
+
|
21
|
+
it "should raise error if :source isn't present" do
|
22
|
+
expect {@dsl.field('test', {:match => /test/, :default => 'test'})}.to raise_error
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
describe :configure do
|
27
|
+
it "should raise an error if :file isn't present" do
|
28
|
+
expect {@dsl.configure({:client_email => '87', :user_email => 'web', :password => 'notasecret'})}.to raise_error
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe GcalMapper::Mapper::Simple do
|
4
|
+
before :all do
|
5
|
+
class User2
|
6
|
+
include GcalMapper::Mapper::Simple
|
7
|
+
attr_accessor :id, :first_name, :name
|
8
|
+
|
9
|
+
end
|
10
|
+
|
11
|
+
@base = GcalMapper::Mapper::Simple::Simple.new(User2)
|
12
|
+
end
|
13
|
+
|
14
|
+
it "should create a new object and save it" do
|
15
|
+
@base.create!({'first_name' => 'a_name', 'name' => 'a_name'})
|
16
|
+
@base.events.should_not eql([])
|
17
|
+
end
|
18
|
+
|
19
|
+
it "shoudl list all stored entry" do
|
20
|
+
@base.find_all.should eql(@base.events)
|
21
|
+
end
|
22
|
+
|
23
|
+
it "should update an entry" do
|
24
|
+
user = @base.events[0]
|
25
|
+
name_before = user.name
|
26
|
+
@base.update!(user.id, {'name' => 'another_name'})
|
27
|
+
name_after = @base.events[0].name
|
28
|
+
|
29
|
+
name_before.should_not eql(name_after)
|
30
|
+
end
|
31
|
+
|
32
|
+
it "should delete an entry" do
|
33
|
+
@base.create!({'first_name' => 'a_name', 'name' => 'a_name'})
|
34
|
+
before_count = @base.events.count
|
35
|
+
user = @base.events[0]
|
36
|
+
@base.delete!(user.id)
|
37
|
+
|
38
|
+
before_count.should > (@base.events.count)
|
39
|
+
end
|
40
|
+
|
41
|
+
it "should find an entry from field name and value" do
|
42
|
+
@base.create!({'first_name' => 'a_name', 'name' => 'another_name'})
|
43
|
+
@base.create!({'first_name' => 'a_name', 'name' => 'different_name'})
|
44
|
+
|
45
|
+
@base.find_by('name', 'a_name').should be_an_instance_of(User2)
|
46
|
+
end
|
47
|
+
|
48
|
+
end
|
data/spec/mapper_spec.rb
ADDED
@@ -0,0 +1,32 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe GcalMapper::Mapper do
|
4
|
+
|
5
|
+
before :all do
|
6
|
+
load File.dirname(__FILE__) + '/support/models/event.rb'
|
7
|
+
end
|
8
|
+
|
9
|
+
it "should load a file containing DSL with no error" do
|
10
|
+
expect{load File.dirname(__FILE__) + '/support/models/event.rb'}.to_not raise_error
|
11
|
+
end
|
12
|
+
|
13
|
+
it "should save all events", :vcr do
|
14
|
+
Event.synchronize_calendar
|
15
|
+
Event.all.should_not eql([])
|
16
|
+
end
|
17
|
+
|
18
|
+
it "should save events only once", :vcr do
|
19
|
+
before = Event.count
|
20
|
+
Event.synchronize_calendar
|
21
|
+
Event.count.should eq(before)
|
22
|
+
end
|
23
|
+
|
24
|
+
it "should erase cancelled event", :vcr do
|
25
|
+
cancelled = 0
|
26
|
+
Event.all.each do |event|
|
27
|
+
cancelled += 1 if event.status == 'cancelled'
|
28
|
+
end
|
29
|
+
cancelled.should eq(0)
|
30
|
+
end
|
31
|
+
|
32
|
+
end
|