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,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
|