rubyzoho 0.1.7 → 0.1.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.
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