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,23 @@
|
|
1
|
+
module GcalMapper
|
2
|
+
#
|
3
|
+
# Gem's configuration
|
4
|
+
#
|
5
|
+
class Configuration
|
6
|
+
|
7
|
+
attr_accessor :gid # name of the field that contains google event id
|
8
|
+
attr_accessor :fields # hash that contains all the field with their source config
|
9
|
+
attr_accessor :calendars # array that contains all the calendar id to fetch
|
10
|
+
attr_accessor :file # Auth file path
|
11
|
+
attr_accessor :client_email # Service account email
|
12
|
+
attr_accessor :user_email # email of user to impersonat
|
13
|
+
attr_accessor :password # passowrd for p12 key
|
14
|
+
|
15
|
+
# New configuration object
|
16
|
+
#
|
17
|
+
def initialize
|
18
|
+
@fields = {}
|
19
|
+
@calendars = []
|
20
|
+
end
|
21
|
+
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
|
2
|
+
|
3
|
+
module GcalMapper
|
4
|
+
#
|
5
|
+
# An error which is raised when the authentification process fails
|
6
|
+
#
|
7
|
+
class AuthentificationError < StandardError
|
8
|
+
end
|
9
|
+
|
10
|
+
#
|
11
|
+
# An error which is raised when the yaml or p12 key does not contains what expected
|
12
|
+
#
|
13
|
+
class AuthFileError < StandardError
|
14
|
+
end
|
15
|
+
|
16
|
+
#
|
17
|
+
# An error which is raised when the http status code of the respose is not within 200..300
|
18
|
+
#
|
19
|
+
class ResponseStatusError < StandardError
|
20
|
+
end
|
21
|
+
|
22
|
+
#
|
23
|
+
# An exception which is raised when no calenards are accessible.
|
24
|
+
#
|
25
|
+
class CalendarAvailabilityError < StandardError
|
26
|
+
end
|
27
|
+
|
28
|
+
#
|
29
|
+
# An exception which is raised when specified calendar is not available.
|
30
|
+
#
|
31
|
+
class CalendarAccessibilityError < StandardError
|
32
|
+
end
|
33
|
+
|
34
|
+
#
|
35
|
+
# Error when there is error in user DSL
|
36
|
+
#
|
37
|
+
class DSLSyntaxError < StandardError
|
38
|
+
end
|
39
|
+
|
40
|
+
end
|
@@ -0,0 +1,76 @@
|
|
1
|
+
require 'gcal_mapper/mapper/dsl'
|
2
|
+
require 'gcal_mapper/mapper/active_record'
|
3
|
+
require 'gcal_mapper/mapper/simple'
|
4
|
+
|
5
|
+
module GcalMapper
|
6
|
+
#
|
7
|
+
# Provide dsl en sync methods
|
8
|
+
#
|
9
|
+
module Mapper
|
10
|
+
|
11
|
+
# setter for class varaible base
|
12
|
+
#
|
13
|
+
# @return [Class] class name of the includer
|
14
|
+
def self.base=(base)
|
15
|
+
@@base = base
|
16
|
+
end
|
17
|
+
|
18
|
+
# reader for class varaible base
|
19
|
+
#
|
20
|
+
# @return [Class] class name of the includer
|
21
|
+
def self.base
|
22
|
+
@@base
|
23
|
+
end
|
24
|
+
|
25
|
+
# setter for config class attributes
|
26
|
+
#
|
27
|
+
# @param [Configuration] config configuration class
|
28
|
+
def self.config=(config)
|
29
|
+
@@config = config
|
30
|
+
end
|
31
|
+
|
32
|
+
# getter for config class attributes
|
33
|
+
#
|
34
|
+
# @return [Configuration] configuration class
|
35
|
+
def self.config
|
36
|
+
@@config
|
37
|
+
end
|
38
|
+
|
39
|
+
# setter for adapter class attributes
|
40
|
+
#
|
41
|
+
# @param [Configuration] config configuration class
|
42
|
+
def self.adapter=(adapter)
|
43
|
+
@@adapter = adapter
|
44
|
+
end
|
45
|
+
|
46
|
+
# getter for adapter class attributes
|
47
|
+
#
|
48
|
+
# @return [Configuration] configuration class
|
49
|
+
def self.adapter
|
50
|
+
@@adapter
|
51
|
+
end
|
52
|
+
|
53
|
+
#
|
54
|
+
# module to add class methods to the includer
|
55
|
+
#
|
56
|
+
module ClassMethods
|
57
|
+
|
58
|
+
# provide dsl function
|
59
|
+
#
|
60
|
+
# @param [Block] block contain dsl of th caller
|
61
|
+
def calendar(&block)
|
62
|
+
Mapper.config = GcalMapper::Configuration.new
|
63
|
+
dsl = DSL.new(Mapper.config)
|
64
|
+
dsl.instance_eval(&block)
|
65
|
+
end
|
66
|
+
|
67
|
+
# synchronize methods for the caller
|
68
|
+
#
|
69
|
+
def synchronize_calendar
|
70
|
+
Sync.sync(Mapper.config, Mapper.base)
|
71
|
+
end
|
72
|
+
|
73
|
+
end
|
74
|
+
|
75
|
+
end
|
76
|
+
end
|
@@ -0,0 +1,74 @@
|
|
1
|
+
require 'active_record'
|
2
|
+
|
3
|
+
module GcalMapper
|
4
|
+
module Mapper
|
5
|
+
#
|
6
|
+
# module to include when active record is in use
|
7
|
+
#
|
8
|
+
module ActiveRecord
|
9
|
+
|
10
|
+
# execute when the file is included
|
11
|
+
#
|
12
|
+
# @param [Class] base class of the includer
|
13
|
+
def self.included(base)
|
14
|
+
base.extend(GcalMapper::Mapper::ClassMethods)
|
15
|
+
Mapper.base = base
|
16
|
+
Mapper.adapter = ActiveRecord.new(base)
|
17
|
+
end
|
18
|
+
|
19
|
+
#
|
20
|
+
# Adapter for ActiveRecord
|
21
|
+
#
|
22
|
+
class ActiveRecord
|
23
|
+
|
24
|
+
def initialize(base)
|
25
|
+
@base = base
|
26
|
+
end
|
27
|
+
|
28
|
+
# create and save object using attributes
|
29
|
+
#
|
30
|
+
# @param [Hash] attributes all the attributes to save in the new object
|
31
|
+
# return [Object] createrd object
|
32
|
+
def create!(attributes)
|
33
|
+
@base.create(attributes)
|
34
|
+
end
|
35
|
+
|
36
|
+
# update an object
|
37
|
+
#
|
38
|
+
# @param [Int] id id of the object in the DB
|
39
|
+
# @param [Hash] attributes hash containing the field to update
|
40
|
+
# @return [Object] updated object
|
41
|
+
def update!(id, attributes)
|
42
|
+
obj = @base.find(id)
|
43
|
+
obj.update_attributes!(attributes)
|
44
|
+
end
|
45
|
+
|
46
|
+
# delet an object
|
47
|
+
#
|
48
|
+
# @param [Int] id id of the object in the DB
|
49
|
+
# @return [Object] the destroyed object
|
50
|
+
def delete!(id)
|
51
|
+
obj = @base.find(id)
|
52
|
+
obj.destroy
|
53
|
+
end
|
54
|
+
|
55
|
+
# Find all models
|
56
|
+
#
|
57
|
+
# @return [Array] all the object from a calss
|
58
|
+
def find_all
|
59
|
+
@base.all
|
60
|
+
end
|
61
|
+
|
62
|
+
# find an object from a field and a value
|
63
|
+
#
|
64
|
+
# @param [String] field name of the field where to search
|
65
|
+
# @param [String] value the value to find
|
66
|
+
# @return [Object] the finded object or nil
|
67
|
+
def find_by(field, value)
|
68
|
+
@base.send('find_by_' + field, value)
|
69
|
+
end
|
70
|
+
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
@@ -0,0 +1,57 @@
|
|
1
|
+
module GcalMapper
|
2
|
+
module Mapper
|
3
|
+
#
|
4
|
+
# Provide DSL to configure gem
|
5
|
+
#
|
6
|
+
class DSL
|
7
|
+
VALID_FIELD_OPTIONS = [:source, :if_empty, :match, :default] # options available in DSL
|
8
|
+
|
9
|
+
# Intitialize config
|
10
|
+
#
|
11
|
+
def initialize(config)
|
12
|
+
@config = config
|
13
|
+
end
|
14
|
+
|
15
|
+
# Add new calendar id
|
16
|
+
#
|
17
|
+
# @param [string] id calendar id to add
|
18
|
+
def calendar (id)
|
19
|
+
@config.calendars.push(id)
|
20
|
+
end
|
21
|
+
|
22
|
+
# Add a new field
|
23
|
+
#
|
24
|
+
# @param [String] name DB field name
|
25
|
+
# @param [Hash] options contains source options used to fill the field
|
26
|
+
def field(name, options = {})
|
27
|
+
raise GcalMapper::DSLSyntaxError, 'Source option must be present' if !options.include?(:source)
|
28
|
+
raise GcalMapper::DSLSyntaxError, 'field option not available' if (options.keys != VALID_FIELD_OPTIONS & options.keys)
|
29
|
+
|
30
|
+
if options[:match]
|
31
|
+
raise GcalMapper::DSLSyntaxError, 'invalid regex' if options[:match].class != Regexp
|
32
|
+
end
|
33
|
+
|
34
|
+
@config.fields.merge!(name => options)
|
35
|
+
end
|
36
|
+
|
37
|
+
# Add authentification configuration
|
38
|
+
#
|
39
|
+
# @param [Hash] options contains the stuff to configure authetification
|
40
|
+
def configure(options = {})
|
41
|
+
raise GcalMapper::DSLSyntaxError, 'you must give a credential file' if options[:file].nil?
|
42
|
+
@config.file = options[:file]
|
43
|
+
@config.client_email = options[:client_email]
|
44
|
+
@config.user_email = options[:user_email]
|
45
|
+
@config.password = options[:password]
|
46
|
+
end
|
47
|
+
|
48
|
+
# Give the field in DB that is reserved to put event id given by google
|
49
|
+
#
|
50
|
+
# @param [String] name DB field name
|
51
|
+
def google_id(name)
|
52
|
+
@config.gid = name
|
53
|
+
end
|
54
|
+
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
@@ -0,0 +1,97 @@
|
|
1
|
+
module GcalMapper
|
2
|
+
module Mapper
|
3
|
+
#
|
4
|
+
# module to include when no orm is used
|
5
|
+
#
|
6
|
+
module Simple
|
7
|
+
|
8
|
+
# execute when the file is included
|
9
|
+
#
|
10
|
+
# @param [Class] base class of the includer
|
11
|
+
def self.included(base)
|
12
|
+
base.extend(GcalMapper::Mapper::ClassMethods)
|
13
|
+
Mapper.base = base
|
14
|
+
Mapper.adapter = Simple.new(base)
|
15
|
+
end
|
16
|
+
|
17
|
+
#
|
18
|
+
# Adapter to use without an orm
|
19
|
+
#
|
20
|
+
class Simple
|
21
|
+
attr_reader :events # array of the synchronized events
|
22
|
+
|
23
|
+
# new object
|
24
|
+
#
|
25
|
+
# @param [class] klass class that need an adapter
|
26
|
+
def initialize(base)
|
27
|
+
@base = base
|
28
|
+
@events = []
|
29
|
+
end
|
30
|
+
|
31
|
+
# create and save object using attributes
|
32
|
+
#
|
33
|
+
# @param [Hash] attributes all the attributes to save in the new object
|
34
|
+
def create!(attributes)
|
35
|
+
obj = @base.new
|
36
|
+
obj.id = @events.count
|
37
|
+
attributes.each do |key, value|
|
38
|
+
obj.send(key+'=', value)
|
39
|
+
end
|
40
|
+
|
41
|
+
@events << obj
|
42
|
+
end
|
43
|
+
|
44
|
+
# update an object
|
45
|
+
#
|
46
|
+
# @param [Int] id id of the object in the DB
|
47
|
+
# @param [Hash] attributes hash containing the field to update
|
48
|
+
def update!(id, attributes)
|
49
|
+
obj = @events[id]
|
50
|
+
attributes.each do |key, value|
|
51
|
+
obj.send(key+'=', value)
|
52
|
+
end
|
53
|
+
|
54
|
+
@events << obj
|
55
|
+
end
|
56
|
+
|
57
|
+
# delet an object
|
58
|
+
#
|
59
|
+
# @param [Int] id id of the object in the DB
|
60
|
+
def delete!(id)
|
61
|
+
@events[id] = nil
|
62
|
+
|
63
|
+
@events.each_with_index do |item, index|
|
64
|
+
if item
|
65
|
+
if @events[index + 1]
|
66
|
+
item = @events[index + 1]
|
67
|
+
item.id =- 1
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
@events.delete(nil)
|
73
|
+
end
|
74
|
+
|
75
|
+
# Find all models
|
76
|
+
#
|
77
|
+
def find_all
|
78
|
+
@events
|
79
|
+
end
|
80
|
+
|
81
|
+
# find an object from a field and a value
|
82
|
+
#
|
83
|
+
# @param [String] field name of the field where to search
|
84
|
+
# @param [String] value the value to find
|
85
|
+
def find_by(field, value)
|
86
|
+
find_event = nil
|
87
|
+
@events.each do |event|
|
88
|
+
find_event = event if event.send(field) == value
|
89
|
+
end
|
90
|
+
|
91
|
+
find_event
|
92
|
+
end
|
93
|
+
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
@@ -0,0 +1,73 @@
|
|
1
|
+
require 'net/http'
|
2
|
+
require 'json'
|
3
|
+
|
4
|
+
module GcalMapper
|
5
|
+
#
|
6
|
+
# Make a REST get or post request
|
7
|
+
#
|
8
|
+
class RestRequest
|
9
|
+
|
10
|
+
attr_accessor :url # Complete url for the request
|
11
|
+
attr_accessor :options # Options for the REST request
|
12
|
+
|
13
|
+
# Initialize a new request
|
14
|
+
#
|
15
|
+
# @param [String] url the complete url of where to send the request
|
16
|
+
# @datarequest.rbparam [Hash] options contain all the options that can be included in REST request
|
17
|
+
def initialize (url, options = {})
|
18
|
+
@url = url
|
19
|
+
@options = options
|
20
|
+
end
|
21
|
+
|
22
|
+
# Set a proxy if the connection needs it
|
23
|
+
#
|
24
|
+
def proxy
|
25
|
+
http_proxy = ENV["http_proxy"]
|
26
|
+
URI.parse(http_proxy) rescue nil
|
27
|
+
end
|
28
|
+
|
29
|
+
# Execute REST request
|
30
|
+
#
|
31
|
+
# @return [Hash] a Json parsed hash that contains the body of the response.
|
32
|
+
def execute
|
33
|
+
options = {
|
34
|
+
:parameters => {}, :debug => false,
|
35
|
+
:http_timeout => 60, :method => :get,
|
36
|
+
:headers => {}, :redirect_count => 0,
|
37
|
+
:max_redirects => 10
|
38
|
+
}.merge(@options)
|
39
|
+
url = URI(@url)
|
40
|
+
|
41
|
+
if proxy
|
42
|
+
http = Net::HTTP::Proxy(proxy.host, proxy.port).new(url.host, url.port)
|
43
|
+
else
|
44
|
+
http = Net::HTTP.new(url.host, url.port)
|
45
|
+
end
|
46
|
+
|
47
|
+
if url.scheme == 'https'
|
48
|
+
http.use_ssl = true
|
49
|
+
end
|
50
|
+
|
51
|
+
http.open_timeout = http.read_timeout = options[:http_timeout]
|
52
|
+
http.set_debug_output $stderr if options[:debug]
|
53
|
+
|
54
|
+
request = case options[:method]
|
55
|
+
when :post
|
56
|
+
request = Net::HTTP::Post.new(url.request_uri)
|
57
|
+
request.set_form_data(options[:parameters])
|
58
|
+
request
|
59
|
+
else
|
60
|
+
request = Net::HTTP::Get.new(url.request_uri)
|
61
|
+
end
|
62
|
+
|
63
|
+
options[:headers].each { |key, value| request[key] = value }
|
64
|
+
response = http.request(request)
|
65
|
+
if !(200..300).include?(response.code.to_i)
|
66
|
+
raise GcalMapper::ResponseStatusError
|
67
|
+
end
|
68
|
+
|
69
|
+
JSON.parse(response.body)
|
70
|
+
end
|
71
|
+
|
72
|
+
end
|
73
|
+
end
|