omnigroupcontacts 0.3.10 → 0.3.11
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.
- checksums.yaml +4 -4
- data/.gitignore +6 -0
- data/Gemfile +3 -0
- data/Gemfile.lock +39 -0
- data/README.md +132 -0
- data/Rakefile +7 -0
- data/lib/omnigroupcontacts.rb +19 -0
- data/lib/omnigroupcontacts/authorization/oauth1.rb +122 -0
- data/lib/omnigroupcontacts/authorization/oauth2.rb +87 -0
- data/lib/omnigroupcontacts/builder.rb +30 -0
- data/lib/omnigroupcontacts/http_utils.rb +101 -0
- data/lib/omnigroupcontacts/importer.rb +5 -0
- data/lib/omnigroupcontacts/importer/gmailgroup.rb +238 -0
- data/lib/omnigroupcontacts/integration_test.rb +36 -0
- data/lib/omnigroupcontacts/middleware/base_oauth.rb +120 -0
- data/lib/omnigroupcontacts/middleware/oauth1.rb +70 -0
- data/lib/omnigroupcontacts/middleware/oauth2.rb +80 -0
- data/lib/omnigroupcontacts/parse_utils.rb +56 -0
- data/omnigroupcontacts-0.3.10.gem +0 -0
- data/omnigroupcontacts-0.3.8.gem +0 -0
- data/omnigroupcontacts-0.3.9.gem +0 -0
- data/omnigroupcontacts.gemspec +25 -0
- data/spec/omnicontacts/authorization/oauth1_spec.rb +82 -0
- data/spec/omnicontacts/authorization/oauth2_spec.rb +92 -0
- data/spec/omnicontacts/http_utils_spec.rb +79 -0
- data/spec/omnicontacts/importer/facebook_spec.rb +120 -0
- data/spec/omnicontacts/importer/gmail_spec.rb +194 -0
- data/spec/omnicontacts/importer/hotmail_spec.rb +106 -0
- data/spec/omnicontacts/importer/linkedin_spec.rb +67 -0
- data/spec/omnicontacts/importer/yahoo_spec.rb +124 -0
- data/spec/omnicontacts/integration_test_spec.rb +51 -0
- data/spec/omnicontacts/middleware/base_oauth_spec.rb +53 -0
- data/spec/omnicontacts/middleware/oauth1_spec.rb +78 -0
- data/spec/omnicontacts/middleware/oauth2_spec.rb +67 -0
- data/spec/omnicontacts/parse_utils_spec.rb +53 -0
- data/spec/spec_helper.rb +12 -0
- metadata +37 -2
@@ -0,0 +1,101 @@
|
|
1
|
+
require "net/http"
|
2
|
+
require "net/https"
|
3
|
+
require "cgi"
|
4
|
+
require "openssl"
|
5
|
+
|
6
|
+
# This module contains a set of utility methods related to the HTTP protocol.
|
7
|
+
module OmniGroupContacts
|
8
|
+
module HTTPUtils
|
9
|
+
|
10
|
+
SSL_PORT = 443
|
11
|
+
|
12
|
+
module_function
|
13
|
+
|
14
|
+
def query_string_to_map query_string
|
15
|
+
query_string.split('&').reduce({}) do |memo, key_value|
|
16
|
+
(key, value) = key_value.split('=')
|
17
|
+
memo[key]= value
|
18
|
+
memo
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def to_query_string map
|
23
|
+
map.collect do |key, value|
|
24
|
+
key.to_s + "=" + value.to_s
|
25
|
+
end.join("&")
|
26
|
+
end
|
27
|
+
|
28
|
+
# Encodes the given input according to RFC 3986
|
29
|
+
def encode to_encode
|
30
|
+
CGI.escape(to_encode)
|
31
|
+
end
|
32
|
+
|
33
|
+
# Calculates the url of the host from a Rack environment.
|
34
|
+
# The result is in the form scheme://host:port
|
35
|
+
# If port is 80 the result is scheme://host
|
36
|
+
# According to Rack specification the HTTP_HOST variable is preferred over SERVER_NAME.
|
37
|
+
def host_url_from_rack_env env
|
38
|
+
port = ((env["SERVER_PORT"] == 80) && "") || ":#{env['SERVER_PORT']}"
|
39
|
+
host = (env["HTTP_HOST"]) || (env["SERVER_NAME"] + port)
|
40
|
+
"#{scheme(env)}://#{host}"
|
41
|
+
end
|
42
|
+
|
43
|
+
def scheme env
|
44
|
+
if env['HTTPS'] == 'on'
|
45
|
+
'https'
|
46
|
+
elsif env['HTTP_X_FORWARDED_SSL'] == 'on'
|
47
|
+
'https'
|
48
|
+
elsif env['HTTP_X_FORWARDED_PROTO']
|
49
|
+
env['HTTP_X_FORWARDED_PROTO'].split(',').first
|
50
|
+
else
|
51
|
+
env["rack.url_scheme"]
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
# Classes including the module must respond to the ssl_ca_file message in order to use the following methods.
|
56
|
+
# The response will be the path to the CA file to use when making https requests.
|
57
|
+
# If the result of ssl_ca_file is nil no file is used. In this case a warn message is logged.
|
58
|
+
private
|
59
|
+
|
60
|
+
# Executes an HTTP GET request.
|
61
|
+
# It raises a RuntimeError if the response code is not equal to 200
|
62
|
+
def http_get host, path, params
|
63
|
+
connection = Net::HTTP.new(host)
|
64
|
+
process_http_response connection.request_get(path + "?" + to_query_string(params))
|
65
|
+
end
|
66
|
+
|
67
|
+
# Executes an HTTP POST request over SSL
|
68
|
+
# It raises a RuntimeError if the response code is not equal to 200
|
69
|
+
def https_post host, path, params
|
70
|
+
https_connection host do |connection|
|
71
|
+
connection.request_post(path, to_query_string(params))
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
# Executes an HTTP GET request over SSL
|
76
|
+
# It raises a RuntimeError if the response code is not equal to 200
|
77
|
+
def https_get host, path, params, headers = nil
|
78
|
+
https_connection host do |connection|
|
79
|
+
connection.request_get(path + "?" + to_query_string(params), headers)
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
def https_connection (host)
|
84
|
+
connection = Net::HTTP.new(host, SSL_PORT)
|
85
|
+
connection.use_ssl = true
|
86
|
+
if ssl_ca_file
|
87
|
+
connection.ca_file = ssl_ca_file
|
88
|
+
else
|
89
|
+
logger << "No SSL ca file provided. It is highly reccomended to use one in production envinronments" if respond_to?(:logger) && logger
|
90
|
+
connection.verify_mode = OpenSSL::SSL::VERIFY_NONE
|
91
|
+
end
|
92
|
+
process_http_response(yield(connection))
|
93
|
+
end
|
94
|
+
|
95
|
+
def process_http_response response
|
96
|
+
raise response.body if response.code != "200"
|
97
|
+
response.body
|
98
|
+
end
|
99
|
+
|
100
|
+
end
|
101
|
+
end
|
@@ -0,0 +1,238 @@
|
|
1
|
+
require "omnigroupcontacts/parse_utils"
|
2
|
+
require "omnigroupcontacts/middleware/oauth2"
|
3
|
+
|
4
|
+
module OmniGroupContacts
|
5
|
+
module Importer
|
6
|
+
class Gmailgroup < Middleware::OAuth2
|
7
|
+
include ParseUtils
|
8
|
+
|
9
|
+
attr_reader :auth_host, :authorize_path, :auth_token_path, :scope
|
10
|
+
|
11
|
+
def initialize *args
|
12
|
+
super *args
|
13
|
+
@auth_host = "accounts.google.com"
|
14
|
+
@authorize_path = "/o/oauth2/auth"
|
15
|
+
@auth_token_path = "/o/oauth2/token"
|
16
|
+
@scope = (args[3] && args[3][:scope]) || "https://www.google.com/m8/feeds https://www.googleapis.com/auth/userinfo#email https://www.googleapis.com/auth/userinfo.profile"
|
17
|
+
@contacts_host = "www.google.com"
|
18
|
+
@contacts_path = "/m8/feeds/groups/default/full"
|
19
|
+
@max_results = (args[3] && args[3][:max_results]) || 100
|
20
|
+
@self_host = "www.googleapis.com"
|
21
|
+
@profile_path = "/oauth2/v1/userinfo"
|
22
|
+
end
|
23
|
+
|
24
|
+
def fetch_groups_using_access_token access_token, token_type
|
25
|
+
fetch_current_user(access_token, token_type)
|
26
|
+
groups_response = https_get(@contacts_host, @contacts_path, contacts_req_params, contacts_req_headers(access_token, token_type))
|
27
|
+
groups_from_response groups_response
|
28
|
+
end
|
29
|
+
|
30
|
+
def fetch_contacts_using_access_token access_token, token_type, group_id
|
31
|
+
fetch_current_user(access_token, token_type)
|
32
|
+
@contacts_path = "/m8/feeds/contacts/default/full?&group=#{group_id}"
|
33
|
+
contacts_response = https_get(@contacts_host, @contacts_path, contacts_req_params, contacts_req_headers(access_token, token_type))
|
34
|
+
contacts_from_response contacts_response
|
35
|
+
end
|
36
|
+
|
37
|
+
def fetch_current_user access_token, token_type
|
38
|
+
self_response = https_get(@self_host, @profile_path, contacts_req_params, contacts_req_headers(access_token, token_type))
|
39
|
+
user = current_user(self_response, access_token, token_type)
|
40
|
+
set_current_user user
|
41
|
+
end
|
42
|
+
|
43
|
+
private
|
44
|
+
|
45
|
+
def contacts_req_params
|
46
|
+
{'max-results' => @max_results.to_s, 'alt' => 'json'}
|
47
|
+
end
|
48
|
+
|
49
|
+
def contacts_req_headers token, token_type
|
50
|
+
{"GData-Version" => "3.0", "Authorization" => "#{token_type} #{token}"}
|
51
|
+
end
|
52
|
+
|
53
|
+
def groups_from_response response_as_json
|
54
|
+
response = JSON.parse(response_as_json)
|
55
|
+
|
56
|
+
return [] if response['feed'].nil? || response['feed']['entry'].nil?
|
57
|
+
groups = []
|
58
|
+
return groups if response.nil?
|
59
|
+
response['feed']['entry'].each do |entry|
|
60
|
+
group = {
|
61
|
+
:id => nil,
|
62
|
+
:title => nil
|
63
|
+
}
|
64
|
+
group[:id] = entry['id']["$t"]
|
65
|
+
group[:title] = entry['title']["$t"]
|
66
|
+
|
67
|
+
groups << group
|
68
|
+
end
|
69
|
+
groups
|
70
|
+
|
71
|
+
end
|
72
|
+
|
73
|
+
def contacts_from_response response_as_json
|
74
|
+
response = JSON.parse(response_as_json)
|
75
|
+
return [] if response['feed'].nil? || response['feed']['entry'].nil?
|
76
|
+
contacts = []
|
77
|
+
return contacts if response.nil?
|
78
|
+
response['feed']['entry'].each do |entry|
|
79
|
+
# creating nil fields to keep the fields consistent across other networks
|
80
|
+
|
81
|
+
contact = { :id => nil,
|
82
|
+
:first_name => nil,
|
83
|
+
:last_name => nil,
|
84
|
+
:name => nil,
|
85
|
+
:emails => nil,
|
86
|
+
:gender => nil,
|
87
|
+
:birthday => nil,
|
88
|
+
:profile_picture=> nil,
|
89
|
+
:relation => nil,
|
90
|
+
:addresses => nil,
|
91
|
+
:phone_numbers => nil,
|
92
|
+
:dates => nil,
|
93
|
+
:company => nil,
|
94
|
+
:position => nil,
|
95
|
+
:groups => nil
|
96
|
+
}
|
97
|
+
contact[:id] = entry['id']['$t'] if entry['id']
|
98
|
+
if entry['gd$name']
|
99
|
+
gd_name = entry['gd$name']
|
100
|
+
contact[:first_name] = normalize_name(entry['gd$name']['gd$givenName']['$t']) if gd_name['gd$givenName']
|
101
|
+
contact[:last_name] = normalize_name(entry['gd$name']['gd$familyName']['$t']) if gd_name['gd$familyName']
|
102
|
+
contact[:name] = normalize_name(entry['gd$name']['gd$fullName']['$t']) if gd_name['gd$fullName']
|
103
|
+
contact[:name] = full_name(contact[:first_name],contact[:last_name]) if contact[:name].nil?
|
104
|
+
end
|
105
|
+
|
106
|
+
contact[:emails] = []
|
107
|
+
entry['gd$email'].each do |email|
|
108
|
+
if email['rel']
|
109
|
+
split_index = email['rel'].index('#')
|
110
|
+
contact[:emails] << {:name => email['rel'][split_index + 1, email['rel'].length - 1], :email => email['address']}
|
111
|
+
elsif email['label']
|
112
|
+
contact[:emails] << {:name => email['label'], :email => email['address']}
|
113
|
+
end
|
114
|
+
end if entry['gd$email']
|
115
|
+
|
116
|
+
# Support older versions of the gem by keeping singular entries around
|
117
|
+
contact[:email] = contact[:emails][0][:email] if contact[:emails][0]
|
118
|
+
contact[:first_name], contact[:last_name], contact[:name] = email_to_name(contact[:name]) if !contact[:name].nil? && contact[:name].include?('@')
|
119
|
+
contact[:first_name], contact[:last_name], contact[:name] = email_to_name(contact[:emails][0][:email]) if (contact[:name].nil? && contact[:emails][0] && contact[:emails][0][:email])
|
120
|
+
#format - year-month-date
|
121
|
+
contact[:birthday] = birthday(entry['gContact$birthday']['when']) if entry['gContact$birthday']
|
122
|
+
|
123
|
+
# value is either "male" or "female"
|
124
|
+
contact[:gender] = entry['gContact$gender']['value'] if entry['gContact$gender']
|
125
|
+
|
126
|
+
if entry['gContact$relation']
|
127
|
+
if entry['gContact$relation'].is_a?(Hash)
|
128
|
+
contact[:relation] = entry['gContact$relation']['rel']
|
129
|
+
elsif entry['gContact$relation'].is_a?(Array)
|
130
|
+
contact[:relation] = entry['gContact$relation'].first['rel']
|
131
|
+
end
|
132
|
+
end
|
133
|
+
|
134
|
+
contact[:addresses] = []
|
135
|
+
entry['gd$structuredPostalAddress'].each do |address|
|
136
|
+
if address['rel']
|
137
|
+
split_index = address['rel'].index('#')
|
138
|
+
new_address = {:name => address['rel'][split_index + 1, address['rel'].length - 1]}
|
139
|
+
elsif address['label']
|
140
|
+
new_address = {:name => address['label']}
|
141
|
+
end
|
142
|
+
|
143
|
+
new_address[:address_1] = address['gd$street']['$t'] if address['gd$street']
|
144
|
+
new_address[:address_1] = address['gd$formattedAddress']['$t'] if new_address[:address_1].nil? && address['gd$formattedAddress']
|
145
|
+
if new_address[:address_1].index("\n")
|
146
|
+
parts = new_address[:address_1].split("\n")
|
147
|
+
new_address[:address_1] = parts.first
|
148
|
+
# this may contain city/state/zip if user jammed it all into one string.... :-(
|
149
|
+
new_address[:address_2] = parts[1..-1].join(', ')
|
150
|
+
end
|
151
|
+
new_address[:city] = address['gd$city']['$t'] if address['gd$city']
|
152
|
+
new_address[:region] = address['gd$region']['$t'] if address['gd$region'] # like state or province
|
153
|
+
new_address[:country] = address['gd$country']['code'] if address['gd$country']
|
154
|
+
new_address[:postcode] = address['gd$postcode']['$t'] if address['gd$postcode']
|
155
|
+
contact[:addresses] << new_address
|
156
|
+
end if entry['gd$structuredPostalAddress']
|
157
|
+
|
158
|
+
# Support older versions of the gem by keeping singular entries around
|
159
|
+
if contact[:addresses][0]
|
160
|
+
contact[:address_1] = contact[:addresses][0][:address_1]
|
161
|
+
contact[:address_2] = contact[:addresses][0][:address_2]
|
162
|
+
contact[:city] = contact[:addresses][0][:city]
|
163
|
+
contact[:region] = contact[:addresses][0][:region]
|
164
|
+
contact[:country] = contact[:addresses][0][:country]
|
165
|
+
contact[:postcode] = contact[:addresses][0][:postcode]
|
166
|
+
end
|
167
|
+
|
168
|
+
contact[:phone_numbers] = []
|
169
|
+
entry['gd$phoneNumber'].each do |phone_number|
|
170
|
+
if phone_number['rel']
|
171
|
+
split_index = phone_number['rel'].index('#')
|
172
|
+
contact[:phone_numbers] << {:name => phone_number['rel'][split_index + 1, phone_number['rel'].length - 1], :number => phone_number['$t']}
|
173
|
+
elsif phone_number['label']
|
174
|
+
contact[:phone_numbers] << {:name => phone_number['label'], :number => phone_number['$t']}
|
175
|
+
end
|
176
|
+
end if entry['gd$phoneNumber']
|
177
|
+
|
178
|
+
# Support older versions of the gem by keeping singular entries around
|
179
|
+
contact[:phone_number] = contact[:phone_numbers][0][:number] if contact[:phone_numbers][0]
|
180
|
+
|
181
|
+
if entry['gContact$website'] && entry['gContact$website'][0]["rel"] == "profile"
|
182
|
+
contact[:id] = contact_id(entry['gContact$website'][0]["href"])
|
183
|
+
contact[:profile_picture] = image_url(contact[:id])
|
184
|
+
else
|
185
|
+
contact[:profile_picture] = image_url_from_email(contact[:email])
|
186
|
+
end
|
187
|
+
|
188
|
+
if entry['gContact$event']
|
189
|
+
contact[:dates] = []
|
190
|
+
entry['gContact$event'].each do |event|
|
191
|
+
if event['rel']
|
192
|
+
contact[:dates] << {:name => event['rel'], :date => birthday(event['gd$when']['startTime'])}
|
193
|
+
elsif event['label']
|
194
|
+
contact[:dates] << {:name => event['label'], :date => birthday(event['gd$when']['startTime'])}
|
195
|
+
end
|
196
|
+
end
|
197
|
+
end
|
198
|
+
|
199
|
+
if entry['gd$organization']
|
200
|
+
contact[:company] = entry['gd$organization'][0]['gd$orgName']['$t'] if entry['gd$organization'][0]['gd$orgName']
|
201
|
+
contact[:position] = entry['gd$organization'][0]['gd$orgTitle']['$t'] if entry['gd$organization'][0]['gd$orgTitle']
|
202
|
+
end
|
203
|
+
|
204
|
+
contacts << contact if contact[:name]
|
205
|
+
end
|
206
|
+
contacts.uniq! {|c| c[:email] || c[:profile_picture] || c[:name]}
|
207
|
+
contacts
|
208
|
+
end
|
209
|
+
|
210
|
+
def image_url gmail_id
|
211
|
+
return "https://profiles.google.com/s2/photos/profile/" + gmail_id if gmail_id
|
212
|
+
end
|
213
|
+
|
214
|
+
def current_user me, access_token, token_type
|
215
|
+
return nil if me.nil?
|
216
|
+
me = JSON.parse(me)
|
217
|
+
user = {:id => me['id'], :email => me['email'], :name => me['name'], :first_name => me['given_name'],
|
218
|
+
:last_name => me['family_name'], :gender => me['gender'], :birthday => birthday(me['birthday']), :profile_picture => image_url(me['id']),
|
219
|
+
:access_token => access_token, :token_type => token_type
|
220
|
+
}
|
221
|
+
user
|
222
|
+
end
|
223
|
+
|
224
|
+
def birthday dob
|
225
|
+
return nil if dob.nil?
|
226
|
+
birthday = dob.split('-')
|
227
|
+
return birthday_format(birthday[2], birthday[3], nil) if birthday.size == 4
|
228
|
+
return birthday_format(birthday[1], birthday[2], birthday[0]) if birthday.size == 3
|
229
|
+
end
|
230
|
+
|
231
|
+
def contact_id(profile_url)
|
232
|
+
id = (profile_url.present?) ? File.basename(profile_url) : nil
|
233
|
+
id
|
234
|
+
end
|
235
|
+
|
236
|
+
end
|
237
|
+
end
|
238
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
require 'singleton'
|
2
|
+
|
3
|
+
class IntegrationTest
|
4
|
+
include Singleton
|
5
|
+
|
6
|
+
attr_accessor :enabled
|
7
|
+
|
8
|
+
def initialize
|
9
|
+
enabled = false
|
10
|
+
clear_mocks
|
11
|
+
end
|
12
|
+
|
13
|
+
def clear_mocks
|
14
|
+
@mock = {}
|
15
|
+
end
|
16
|
+
|
17
|
+
def mock provider, mock
|
18
|
+
@mock[provider.to_sym] = mock
|
19
|
+
end
|
20
|
+
|
21
|
+
def mock_authorization_from_user provider
|
22
|
+
[302, {"Content-Type" => "application/x-www-form-urlencoded", "location" => provider.redirect_path}, []]
|
23
|
+
end
|
24
|
+
|
25
|
+
def mock_fetch_contacts provider
|
26
|
+
result = @mock[provider.class_name.to_sym] || []
|
27
|
+
if result.is_a? Array
|
28
|
+
result
|
29
|
+
elsif result.is_a? Hash
|
30
|
+
[result]
|
31
|
+
else
|
32
|
+
raise result.to_s
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
end
|
@@ -0,0 +1,120 @@
|
|
1
|
+
# This class contains the common behavior for middlewares
|
2
|
+
# implementing either versions of OAuth.
|
3
|
+
#
|
4
|
+
# Extending classes are required to implement
|
5
|
+
# the following methods:
|
6
|
+
# * request_authorization_from_user
|
7
|
+
# * fetch_contatcs
|
8
|
+
module OmniGroupContacts
|
9
|
+
module Middleware
|
10
|
+
class BaseOAuth
|
11
|
+
|
12
|
+
attr_reader :ssl_ca_file
|
13
|
+
|
14
|
+
def initialize app, options
|
15
|
+
@app = app
|
16
|
+
@listening_path = MOUNT_PATH + class_name
|
17
|
+
@ssl_ca_file = options[:ssl_ca_file]
|
18
|
+
end
|
19
|
+
|
20
|
+
def class_name
|
21
|
+
self.class.name.split('::').last.downcase
|
22
|
+
end
|
23
|
+
|
24
|
+
# Rack callback. It handles three cases:
|
25
|
+
# * user visit middleware entry point.
|
26
|
+
# In this case request_authorization_from_user is called
|
27
|
+
# * user is redirected back to the application
|
28
|
+
# from the authorization site. In this case the list
|
29
|
+
# of contacts is fetched and stored in the variables
|
30
|
+
# omnigroupcontacts.contacts within the Rack env variable.
|
31
|
+
# Once that is done the next middleware component is called.
|
32
|
+
# * user visits any other resource. In this case the request
|
33
|
+
# is simply forwarded to the next middleware component.
|
34
|
+
def call env
|
35
|
+
@env = env
|
36
|
+
if env["PATH_INFO"] =~ /^#{@listening_path}\/?$/
|
37
|
+
handle_initial_request
|
38
|
+
elsif env["PATH_INFO"] =~ /^#{redirect_path}/
|
39
|
+
handle_callback
|
40
|
+
else
|
41
|
+
@app.call(env)
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
private
|
46
|
+
|
47
|
+
def test_mode?
|
48
|
+
IntegrationTest.instance.enabled
|
49
|
+
end
|
50
|
+
|
51
|
+
def handle_initial_request
|
52
|
+
execute_and_rescue_exceptions do
|
53
|
+
if test_mode?
|
54
|
+
IntegrationTest.instance.mock_authorization_from_user(self)
|
55
|
+
else
|
56
|
+
request_authorization_from_user
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
def handle_callback
|
62
|
+
execute_and_rescue_exceptions do
|
63
|
+
@env["omnicontacts.contacts"] = if test_mode?
|
64
|
+
IntegrationTest.instance.mock_fetch_contacts(self)
|
65
|
+
else
|
66
|
+
fetch_groups
|
67
|
+
end
|
68
|
+
@app.call(@env)
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
def set_current_user user
|
73
|
+
@env["omnigroupcontacts.user"] = user
|
74
|
+
end
|
75
|
+
|
76
|
+
# This method rescues executes a block of code and
|
77
|
+
# rescue all exceptions. In case of an exception the
|
78
|
+
# user is redirected to the failure endpoint.
|
79
|
+
def execute_and_rescue_exceptions
|
80
|
+
yield
|
81
|
+
rescue AuthorizationError => e
|
82
|
+
handle_error :not_authorized, e
|
83
|
+
rescue ::Timeout::Error, ::Errno::ETIMEDOUT => e
|
84
|
+
handle_error :timeout, e
|
85
|
+
rescue ::RuntimeError => e
|
86
|
+
handle_error :internal_error, e
|
87
|
+
end
|
88
|
+
|
89
|
+
def handle_error error_type, exception
|
90
|
+
logger.puts("Error #{error_type} while processing #{@env["PATH_INFO"]}: #{exception.message}") if logger
|
91
|
+
failure_url = "#{ MOUNT_PATH }failure?error_message=#{error_type}&importer=#{class_name}"
|
92
|
+
target_url = append_state_query(failure_url)
|
93
|
+
[302, {"Content-Type" => "text/html", "location" => target_url}, []]
|
94
|
+
end
|
95
|
+
|
96
|
+
def session
|
97
|
+
raise "You must provide a session to use omnigroupcontacts" unless @env["rack.session"]
|
98
|
+
@env["rack.session"]
|
99
|
+
end
|
100
|
+
|
101
|
+
def logger
|
102
|
+
@env["rack.errors"] if @env
|
103
|
+
end
|
104
|
+
|
105
|
+
def base_prop_name
|
106
|
+
"omnigroupcontacts." + class_name
|
107
|
+
end
|
108
|
+
|
109
|
+
def append_state_query(target_url)
|
110
|
+
state = Rack::Utils.parse_query(@env['QUERY_STRING'])['state']
|
111
|
+
|
112
|
+
unless state.nil?
|
113
|
+
target_url = target_url + (target_url.include?("?")?"&":"?") + 'state=' + state
|
114
|
+
end
|
115
|
+
|
116
|
+
return target_url
|
117
|
+
end
|
118
|
+
end
|
119
|
+
end
|
120
|
+
end
|