rubyzoho 0.1.7 → 0.1.11

Sign up to get free protection for your applications and to get access to all the features.
data/lib/zoho_api.rb CHANGED
@@ -8,62 +8,47 @@ require 'mime/types'
8
8
  require 'ruby_zoho'
9
9
  require 'yaml'
10
10
  require 'api_utils'
11
+ require 'zoho_api_field_utils'
12
+ require 'zoho_api_finders'
11
13
 
12
14
  module ZohoApi
13
15
 
16
+
14
17
  include ApiUtils
15
18
 
16
19
  class Crm
17
- NUMBER_OF_RECORDS_TO_GET = 200
18
20
 
19
21
  include HTTMultiParty
20
-
21
- @@module_fields = {}
22
- @@users = []
22
+ include ZohoApiFieldUtils
23
+ include ZohoApiFinders
23
24
 
24
25
  #debug_output $stderr
25
26
 
26
27
  attr_reader :auth_token, :module_fields
27
28
 
28
- def initialize(auth_token, modules, fields = nil)
29
+ def initialize(auth_token, modules, ignore_fields, fields = nil)
29
30
  @auth_token = auth_token
30
31
  @modules = %w(Accounts Contacts Events Leads Potentials Tasks Users).concat(modules).uniq
31
32
  @module_fields = fields.nil? ? reflect_module_fields : fields
33
+ @ignore_fields = ignore_fields
32
34
  end
33
35
 
34
36
  def add_record(module_name, fields_values_hash)
35
37
  x = REXML::Document.new
36
38
  element = x.add_element module_name
37
- row = element.add_element 'row', { 'no' => '1'}
39
+ row = element.add_element 'row', { 'no' => '1' }
38
40
  fields_values_hash.each_pair { |k, v| add_field(row, ApiUtils.symbol_to_string(k), v) }
39
41
  r = self.class.post(create_url(module_name, 'insertRecords'),
40
- :query => { :newFormat => 1, :authtoken => @auth_token,
41
- :scope => 'crmapi', :xmlData => x },
42
- :headers => { 'Content-length' => '0' })
42
+ :query => { :newFormat => 1, :authtoken => @auth_token,
43
+ :scope => 'crmapi', :xmlData => x },
44
+ :headers => { 'Content-length' => '0' })
43
45
  check_for_errors(r)
44
46
  x_r = REXML::Document.new(r.body).elements.to_a('//recorddetail')
45
47
  to_hash(x_r, module_name)[0]
46
48
  end
47
49
 
48
- def add_field(row, field, value)
49
- r = (REXML::Element.new 'FL')
50
- adjust_tag_case(field)
51
- r.attributes['val'] = adjust_tag_case(field)
52
- r.add_text("#{value}")
53
- row.elements << r
54
- row
55
- end
56
-
57
- def adjust_tag_case(tag)
58
- return tag if tag == 'id'
59
- return tag.upcase if tag.downcase.rindex('id')
60
- u_tags = %w[SEMODULE]
61
- return tag.upcase if u_tags.index(tag.upcase)
62
- tag
63
- end
64
-
65
50
  def attach_file(module_name, record_id, file_path, file_name)
66
- mime_type = (MIME::Types.type_for(file_path)[0] || MIME::Types["application/octet-stream"][0])
51
+ mime_type = (MIME::Types.type_for(file_path)[0] || MIME::Types['application/octet-stream'][0])
67
52
  url_path = create_url(module_name, "uploadFile?authtoken=#{@auth_token}&scope=crmapi&id=#{record_id}")
68
53
  url = URI.parse(create_url(module_name, url_path))
69
54
  io = UploadIO.new(file_path, mime_type, file_name)
@@ -80,9 +65,8 @@ module ZohoApi
80
65
  def check_for_errors(response)
81
66
  raise(RuntimeError, "Web service call failed with #{response.code}") unless response.code == 200
82
67
  x = REXML::Document.new(response.body)
83
- code = REXML::XPath.first(x, '//code')
84
- raise(RuntimeError, "Zoho Error Code #{code.text}: #{REXML::XPath.first(x, '//message').text}.") unless
85
- code.nil? || ['4422', '5000'].index(code.text)
68
+ code = REXML::XPath.first(x, '//code')
69
+ raise(RuntimeError, "Zoho Error Code #{code.text}: #{REXML::XPath.first(x, '//message').text}.") unless code.nil? || ['4422', '5000'].index(code.text)
86
70
  return code.text unless code.nil?
87
71
  response.code
88
72
  end
@@ -95,85 +79,19 @@ module ZohoApi
95
79
  post_action(module_name, record_id, 'deleteRecords')
96
80
  end
97
81
 
98
- def fields(module_name)
99
- return user_fields if module_name == 'Users'
100
- fields_from_record(module_name).nil? ? fields_from_api(module_name) : fields_from_record(module_name)
101
- end
102
-
103
- def fields_from_api(module_name)
104
- mod_name = ApiUtils.string_to_symbol(module_name)
105
- return @@module_fields[mod_name] unless @@module_fields[mod_name].nil?
106
- r = self.class.post(create_url(module_name, 'getFields'),
107
- :query => { :authtoken => @auth_token, :scope => 'crmapi' },
108
- :headers => { 'Content-length' => '0' })
109
- check_for_errors(r)
110
- update_module_fields(mod_name, module_name, r)
111
- end
112
-
113
- def fields_from_record(module_name)
114
- mod_name = ApiUtils.string_to_symbol(module_name)
115
- return @@module_fields[mod_name] unless @@module_fields[mod_name].nil?
116
- r = first(module_name)
117
- return nil if r.nil?
118
- @@module_fields[mod_name] = r.first.keys
119
- @@module_fields[mod_name]
120
- end
121
-
122
82
  def first(module_name)
123
83
  some(module_name, 1, 1)
124
84
  end
125
85
 
126
- def find_records(module_name, field, condition, value)
127
- sc_field = field == :id ? primary_key(module_name) : ApiUtils.symbol_to_string(field)
128
- return find_record_by_related_id(module_name, sc_field, value) if related_id?(module_name, sc_field)
129
- primary_key?(module_name, sc_field) == false ? find_record_by_field(module_name, sc_field, condition, value) :
130
- find_record_by_id(module_name, value)
131
- end
132
-
133
- def find_record_by_field(module_name, sc_field, condition, value)
134
- field = sc_field.rindex('id') ? sc_field.downcase : sc_field
135
- search_condition = '(' + field + '|' + condition + '|' + value + ')'
136
- r = self.class.get(create_url("#{module_name}", 'getSearchRecords'),
137
- :query => {:newFormat => 1, :authtoken => @auth_token, :scope => 'crmapi',
138
- :selectColumns => 'All', :searchCondition => search_condition,
139
- :fromIndex => 1, :toIndex => NUMBER_OF_RECORDS_TO_GET})
140
- check_for_errors(r)
141
- x = REXML::Document.new(r.body).elements.to_a("/response/result/#{module_name}/row")
142
- to_hash(x, module_name)
143
- end
144
-
145
- def find_record_by_id(module_name, id)
146
- r = self.class.get(create_url("#{module_name}", 'getRecordById'),
147
- :query => { :newFormat => 1, :authtoken => @auth_token, :scope => 'crmapi',
148
- :selectColumns => 'All', :id => id})
149
- raise(RuntimeError, 'Bad query', "#{module_name} #{id}") unless r.body.index('<error>').nil?
150
- check_for_errors(r)
151
- x = REXML::Document.new(r.body).elements.to_a("/response/result/#{module_name}/row")
152
- to_hash(x, module_name)
153
- end
154
-
155
- def find_record_by_related_id(module_name, sc_field, value)
156
- raise(RuntimeError, "[RubyZoho] Not a valid query field #{sc_field} for module #{module_name}") unless
157
- valid_related?(module_name, sc_field)
158
- field = sc_field.downcase
159
- r = self.class.get(create_url("#{module_name}", 'getSearchRecordsByPDC'),
160
- :query => { :newFormat => 1, :authtoken => @auth_token, :scope => 'crmapi',
161
- :selectColumns => 'All', :version => 2, :searchColumn => field,
162
- :searchValue => value})
163
- check_for_errors(r)
164
- x = REXML::Document.new(r.body).elements.to_a("/response/result/#{module_name}/row")
165
- to_hash(x, module_name)
166
- end
167
-
168
86
  def method_name?(n)
169
87
  return /[@$"]/ !~ n.inspect
170
88
  end
171
89
 
172
90
  def post_action(module_name, record_id, action_type)
173
91
  r = self.class.post(create_url(module_name, action_type),
174
- :query => {:newFormat => 1, :authtoken => @auth_token,
175
- :scope => 'crmapi', :id => record_id},
176
- :headers => {'Content-length' => '0'})
92
+ :query => { :newFormat => 1, :authtoken => @auth_token,
93
+ :scope => 'crmapi', :id => record_id },
94
+ :headers => { 'Content-length' => '0' })
177
95
  raise('Adding contact failed', RuntimeError, r.response.body.to_s) unless r.response.code == '200'
178
96
  check_for_errors(r)
179
97
  end
@@ -199,101 +117,70 @@ module ZohoApi
199
117
  field.downcase.gsub('id', '') != module_name.chop.downcase
200
118
  end
201
119
 
202
- def reflect_module_fields
203
- @modules.each { |m| fields(m) }
204
- @@module_fields
205
- end
206
-
207
120
  def related_records(parent_module, parent_record_id, related_module)
208
121
  r = self.class.get(create_url("#{related_module}", 'getRelatedRecords'),
209
- :query => { :newFormat => 1, :authtoken => @auth_token, :scope => 'crmapi',
210
- :parentModule => parent_module, :id => parent_record_id})
122
+ :query => { :newFormat => 1, :authtoken => @auth_token, :scope => 'crmapi',
123
+ :parentModule => parent_module, :id => parent_record_id })
211
124
 
212
125
  x = REXML::Document.new(r.body).elements.to_a("/response/result/#{parent_module}/row")
213
126
  check_for_errors(r)
214
127
  end
215
128
 
216
- def some(module_name, index = 1, number_of_records = nil)
129
+ def some(module_name, index = 1, number_of_records = nil, sort_column = :id, sort_order = :asc)
217
130
  r = self.class.get(create_url(module_name, 'getRecords'),
218
- :query => { :newFormat => 1, :authtoken => @auth_token, :scope => 'crmapi',
219
- :fromIndex => index, :toIndex => number_of_records || NUMBER_OF_RECORDS_TO_GET })
131
+ :query => { :newFormat => 2, :authtoken => @auth_token, :scope => 'crmapi',
132
+ :sortColumnString => sort_column, :sortOrderString => sort_order,
133
+ :fromIndex => index, :toIndex => number_of_records || NUMBER_OF_RECORDS_TO_GET })
220
134
  return nil unless r.response.code == '200'
221
135
  check_for_errors(r)
222
136
  x = REXML::Document.new(r.body).elements.to_a("/response/result/#{module_name}/row")
223
137
  to_hash(x, module_name)
224
138
  end
225
139
 
226
- def to_hash(xml_results, module_name)
227
- r = []
228
- xml_results.each do |e|
229
- record = {}
230
- record[:module_name] = module_name
231
- e.elements.to_a.each do |n|
232
- k = ApiUtils.string_to_symbol(n.attribute('val').to_s.gsub('val=', ''))
233
- v = n.text == 'null' ? nil : n.text
234
- record.merge!({ k => v })
235
- record.merge!({ :id => v }) if primary_key?(module_name, k)
236
- end
237
- r << record
238
- end
239
- return nil if r == []
240
- r
241
- end
242
-
243
- def to_hash_with_id(xml_results, module_name)
244
- to_hash(xml_results, module_name)
245
- end
246
-
247
- def update_module_fields(mod_name, module_name, r)
248
- @@module_fields[mod_name] = []
249
- x = REXML::Document.new(r.body)
250
- REXML::XPath.each(x, "/#{module_name}/section/FL/@dv") do |f|
251
- field = ApiUtils.string_to_symbol(f.to_s)
252
- @@module_fields[mod_name] << field if method_name?(field)
253
- end
254
- @@module_fields[mod_name] << ApiUtils.string_to_symbol(module_name.chop + 'id')
255
- return @@module_fields[mod_name] unless @@module_fields.nil?
256
- nil
257
- end
258
-
259
140
  def update_record(module_name, id, fields_values_hash)
260
141
  x = REXML::Document.new
261
142
  contacts = x.add_element module_name
262
- row = contacts.add_element 'row', { 'no' => '1'}
143
+ row = contacts.add_element 'row', { 'no' => '1' }
263
144
  fields_values_hash.each_pair { |k, v| add_field(row, ApiUtils.symbol_to_string(k), v) }
264
145
  r = self.class.post(create_url(module_name, 'updateRecords'),
265
- :query => { :newFormat => 1, :authtoken => @auth_token,
266
- :scope => 'crmapi', :id => id,
267
- :xmlData => x },
268
- :headers => { 'Content-length' => '0' })
146
+ :query => { :newFormat => 1, :authtoken => @auth_token,
147
+ :scope => 'crmapi', :id => id,
148
+ :xmlData => x },
149
+ :headers => { 'Content-length' => '0' })
269
150
  check_for_errors(r)
270
151
  x_r = REXML::Document.new(r.body).elements.to_a('//recorddetail')
271
152
  to_hash_with_id(x_r, module_name)[0]
272
153
  end
273
154
 
274
- def user_fields
275
- @@module_fields[:users] = users[0].keys
276
- end
277
-
278
155
  def users(user_type = 'AllUsers')
279
156
  return @@users unless @@users == [] || user_type == 'Refresh'
280
157
  r = self.class.get(create_url('Users', 'getUsers'),
281
- :query => { :newFormat => 1, :authtoken => @auth_token, :scope => 'crmapi',
282
- :type => 'AllUsers' })
158
+ :query => { :newFormat => 1, :authtoken => @auth_token, :scope => 'crmapi',
159
+ :type => 'AllUsers' })
283
160
  check_for_errors(r)
284
- x = REXML::Document.new(r.body).elements.to_a("/users")
161
+ result = extract_users_from_xml_response(r)
162
+ @@users = result
163
+ end
164
+
165
+ def extract_users_from_xml_response(response)
166
+ x = REXML::Document.new(response.body).elements.to_a('/users')
285
167
  result = []
286
168
  x.each do |e|
287
- e.elements.to_a.each do |n|
288
- record = {}
289
- record.merge!( { :user_name => n.text })
290
- n.attributes.each_pair do |k, v|
291
- record.merge!({ k.to_s.to_sym => v.to_string.match(/'(.*?)'/).to_s.gsub("'", '') })
292
- end
169
+ e.elements.to_a.each do |node|
170
+ record = extract_user_name_and_attribs(node)
293
171
  result << record
294
172
  end
295
173
  end
296
- @@users = result
174
+ result
175
+ end
176
+
177
+ def extract_user_name_and_attribs(node)
178
+ record = {}
179
+ record.merge!({ :user_name => node.text })
180
+ node.attributes.each_pair do |k, v|
181
+ record.merge!({ k.to_s.to_sym => v.to_string.match(/'(.*?)'/).to_s.gsub('\'', '') })
182
+ end
183
+ record
297
184
  end
298
185
 
299
186
  def valid_related?(module_name, field)
@@ -322,3 +209,4 @@ module ZohoApi
322
209
  end
323
210
 
324
211
  end
212
+
@@ -0,0 +1,126 @@
1
+ module ZohoApiFieldUtils
2
+
3
+ @@module_fields = {}
4
+ @@users = []
5
+
6
+ def add_field(row, field, value)
7
+ r = (REXML::Element.new 'FL')
8
+ adjust_tag_case(field)
9
+ r.attributes['val'] = adjust_tag_case(field)
10
+ r.add_text("#{value}")
11
+ row.elements << r
12
+ row
13
+ end
14
+
15
+ def adjust_tag_case(tag)
16
+ return tag if tag == 'id'
17
+ return tag.upcase if tag.downcase.rindex('id')
18
+ u_tags = %w[SEMODULE]
19
+ return tag.upcase if u_tags.index(tag.upcase)
20
+ tag
21
+ end
22
+
23
+ def clean_field_name?(field_name)
24
+ return false if field_name.nil?
25
+ r = field_name[/[0-9, a-z, A-Z, _]*/]
26
+ field_name.size == r.size
27
+ end
28
+
29
+ def fields(module_name)
30
+ return user_fields if module_name == 'Users'
31
+ fields_from_record(module_name).nil? ? fields_from_api(module_name) : fields_from_record(module_name)
32
+ end
33
+
34
+ def fields_original(module_name)
35
+ return nil if @@module_fields.nil?
36
+ #return user_fields if module_name == 'Users'
37
+ @@module_fields[module_name + '_original_name']
38
+ end
39
+
40
+ def fields_from_api(module_name)
41
+ mod_name = ApiUtils.string_to_symbol(module_name)
42
+ return @@module_fields[mod_name] unless @@module_fields[mod_name].nil?
43
+ r = self.class.post(create_url(module_name, 'getFields'),
44
+ :query => { :authtoken => @auth_token, :scope => 'crmapi' },
45
+ :headers => { 'Content-length' => '0' })
46
+ check_for_errors(r)
47
+ update_module_fields(mod_name, module_name, r)
48
+ end
49
+
50
+ def fields_from_record(module_name)
51
+ mod_name = ApiUtils.string_to_symbol(module_name)
52
+ return @@module_fields[mod_name] unless @@module_fields[mod_name].nil?
53
+ r = first(module_name)
54
+ return nil if r.nil?
55
+ @@module_fields[mod_name] = r.first.keys
56
+ @@module_fields[mod_name]
57
+ end
58
+
59
+ def hashed_field_value_pairs(module_name, n, record)
60
+ field_name = n.attribute('val').to_s.gsub('val=', '')
61
+ if @ignore_fields == true
62
+ return clean_field_name?(field_name) == true ?
63
+ create_and_add_field_value_pair(field_name, module_name, n, record)
64
+ : record
65
+ end
66
+ create_and_add_field_value_pair(field_name, module_name, n, record)
67
+ end
68
+
69
+ def create_and_add_field_value_pair(field_name, module_name, n, record)
70
+ k = ApiUtils.string_to_symbol(field_name)
71
+ v = n.text == 'null' ? nil : n.text
72
+ r = record.merge({ k => v })
73
+ r = r.merge({ :id => v }) if primary_key?(module_name, k)
74
+ r
75
+ end
76
+
77
+ def reflect_module_fields
78
+ @modules.each { |m| fields(m) }
79
+ @@module_fields
80
+ end
81
+
82
+ def extract_fields_from_response(mod_name, module_name, response)
83
+ x = REXML::Document.new(response.body)
84
+ REXML::XPath.each(x, "/#{module_name}/section/FL/@dv") do |field|
85
+ extract_field(field, mod_name)
86
+ end
87
+ @@module_fields[mod_name] << ApiUtils.string_to_symbol(module_name.chop + 'id')
88
+ end
89
+
90
+ def extract_field(f, mod_name)
91
+ field = ApiUtils.string_to_symbol(f.to_s)
92
+ @@module_fields[mod_name] << field if method_name?(field)
93
+ @@module_fields[(mod_name.to_s + '_original_name').to_sym] << field
94
+ end
95
+
96
+ def to_hash(xml_results, module_name)
97
+ r = []
98
+ xml_results.each do |e|
99
+ record = {}
100
+ record[:module_name] = module_name
101
+ e.elements.to_a.each do |n|
102
+ record = hashed_field_value_pairs(module_name, n, record)
103
+ end
104
+ r << record unless record.nil?
105
+ end
106
+ return nil if r == []
107
+ r
108
+ end
109
+
110
+ def to_hash_with_id(xml_results, module_name)
111
+ to_hash(xml_results, module_name)
112
+ end
113
+
114
+ def update_module_fields(mod_name, module_name, response)
115
+ @@module_fields[mod_name] = []
116
+ @@module_fields[(mod_name.to_s + '_original_name').to_sym] = []
117
+ extract_fields_from_response(mod_name, module_name, response)
118
+ return @@module_fields[mod_name] unless @@module_fields.nil?
119
+ nil
120
+ end
121
+
122
+ def user_fields
123
+ @@module_fields[:users] = users[0].keys
124
+ end
125
+
126
+ end
@@ -0,0 +1,45 @@
1
+ module ZohoApiFinders
2
+ NUMBER_OF_RECORDS_TO_GET = 200
3
+
4
+ def find_records(module_name, field, condition, value)
5
+ sc_field = field == :id ? primary_key(module_name) : ApiUtils.symbol_to_string(field)
6
+ return find_record_by_related_id(module_name, sc_field, value) if related_id?(module_name, sc_field)
7
+ primary_key?(module_name, sc_field) == false ? find_record_by_field(module_name, sc_field, condition, value) :
8
+ find_record_by_id(module_name, value)
9
+ end
10
+
11
+ def find_record_by_field(module_name, sc_field, condition, value)
12
+ field = sc_field.rindex('id') ? sc_field.downcase : sc_field
13
+ search_condition = '(' + field + '|' + condition + '|' + value + ')'
14
+ r = self.class.get(create_url("#{module_name}", 'getSearchRecords'),
15
+ :query => { :newFormat => 1, :authtoken => @auth_token, :scope => 'crmapi',
16
+ :selectColumns => 'All', :searchCondition => search_condition,
17
+ :fromIndex => 1, :toIndex => NUMBER_OF_RECORDS_TO_GET })
18
+ check_for_errors(r)
19
+ x = REXML::Document.new(r.body).elements.to_a("/response/result/#{module_name}/row")
20
+ to_hash(x, module_name)
21
+ end
22
+
23
+ def find_record_by_id(module_name, id)
24
+ r = self.class.get(create_url("#{module_name}", 'getRecordById'),
25
+ :query => { :newFormat => 1, :authtoken => @auth_token, :scope => 'crmapi',
26
+ :selectColumns => 'All', :id => id })
27
+ raise(RuntimeError, 'Bad query', "#{module_name} #{id}") unless r.body.index('<error>').nil?
28
+ check_for_errors(r)
29
+ x = REXML::Document.new(r.body).elements.to_a("/response/result/#{module_name}/row")
30
+ to_hash(x, module_name)
31
+ end
32
+
33
+ def find_record_by_related_id(module_name, sc_field, value)
34
+ raise(RuntimeError, "[RubyZoho] Not a valid query field #{sc_field} for module #{module_name}") unless valid_related?(module_name, sc_field)
35
+ field = sc_field.downcase
36
+ r = self.class.get(create_url("#{module_name}", 'getSearchRecordsByPDC'),
37
+ :query => { :newFormat => 1, :authtoken => @auth_token, :scope => 'crmapi',
38
+ :selectColumns => 'All', :version => 2, :searchColumn => field,
39
+ :searchValue => value })
40
+ check_for_errors(r)
41
+ x = REXML::Document.new(r.body).elements.to_a("/response/result/#{module_name}/row")
42
+ to_hash(x, module_name)
43
+ end
44
+
45
+ end