gcal_mapper 0.1.1
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.
- 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
|