rhoconnect-adapters 1.0.5 → 1.0.6

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 CHANGED
@@ -2,3 +2,5 @@
2
2
  .bundle
3
3
  Gemfile.lock
4
4
  pkg/*
5
+ TODO
6
+ *~
data/CHANGELOG CHANGED
@@ -1,3 +1,8 @@
1
+ ## 1.0.6 (11-28-2012)
2
+ * Rework adapter method 'query' be able to paginate into (large) result sets for oracle, sugar, salesforce vendors
3
+ * Updated salesforce SOAP service version to v24.0 (it provides API paginate into backend data)
4
+ * Fixed not working specs under Linux
5
+
1
6
  ## 1.0.5
2
7
  * support for rhoconnect >= 3.2
3
8
 
@@ -22,4 +27,4 @@
22
27
  ## 1.0.0
23
28
  * Integration of Salesforce, MsDynamics, Sugar backends
24
29
  * #15178091 - Integration of OracleCRM backend, specs for generator
25
- * #14613211 - Initial creation of the rhoconnect-adapters
30
+ * #14613211 - Initial creation of the rhoconnect-adapters
data/README.md CHANGED
@@ -123,7 +123,7 @@ These are:
123
123
  - **:msdynamics_ticket_url:** <msdynamics_web_services_integration_url> - substitute the default URL with your MsDynamics account URL.
124
124
 
125
125
  For every source adapter based on CRM object there is a corresponding *'vendor/msdynamics/settings/\<CRM\-object\-name\>.yml'*
126
- file containing the descriptions for the Sugar CRM object.
126
+ file containing the descriptions for the MsDynamics object.
127
127
  Every CRM object file has the following entries:
128
128
 
129
129
  Query_Fields: hash of the objects's fields
@@ -199,7 +199,7 @@ In the file `settings.yml` you'll find the entries that you must customize befor
199
199
  These are:
200
200
 
201
201
  - **:salesforce_login_url:** <salesforce_soap_login_url> - Currently, this parameter is pre-defined to
202
- `https://login.salesforce.com/services/Soap/c/22.0` for simple SOAP web service authentication.
202
+ `https://login.salesforce.com/services/Soap/c/24.0` for simple SOAP web service authentication.
203
203
  Rhoconnect-adapters is not using `OAuth2` scheme at this point.
204
204
 
205
205
  For every source adapter based on CRM object there is a corresponding *'vendor/salesforce/settings/\<CRM\-object\-name\>.yml'*
@@ -229,6 +229,73 @@ different fields. For custom adapters, you need to fill this file with relevant
229
229
  from the Salesforce documentation and then later used to fill the Query_Fields setting. Alternatively, user can customize the adapter and obtain
230
230
  the list of fields using the `/sobjects/<CRM-object-name>/describe/` API.
231
231
 
232
+ ## If you have large dataset on backend
233
+ If your application's model has a lot of data, when you might wanted to paginate through backend service data instead of fetching everything in one call. RhoConnect API provides
234
+ [ `stash_result`](http://docs.rhomobile.com/rhoconnect/source-adapters#source-adapter-api) method to do it.
235
+
236
+ For example, straightforward way to query data in Salesforce CRM adapter is
237
+
238
+ :::ruby
239
+ def query(params=nil)
240
+ @result = {}
241
+ fieldquery = ""
242
+ @fields.each do |element_name, element_def|
243
+ fieldquery << ",#{element_name}"
244
+ end
245
+ fieldquery[0] = " "
246
+
247
+ querystr = "SELECT #{fieldquery} FROM #{crm_object}"
248
+ requesturl = @resturl + "/query/?q=" + CGI::escape(querystr)
249
+ raw_data = RestClient.get(requesturl, @restheaders)
250
+ parsed_data = JSON.parse raw_data
251
+
252
+ if parsed_data['done']
253
+ parsed_data["records"].each do |record|
254
+ record_hash = {}
255
+ @fields.each do |element_name, element_def|
256
+ record_hash[element_name] = record[element_name]
257
+ end
258
+ @result[record['Id']] = record_hash
259
+ end
260
+ else
261
+ # TODO: queryMore
262
+ end
263
+ @result
264
+ end
265
+
266
+ In the case, if you wanted paginate through backend data the same `query` method should be rewritten as
267
+
268
+ :::ruby
269
+ def query(params=nil)
270
+ fieldquery = ""
271
+ @fields.each do |element_name, element_def|
272
+ fieldquery << ",#{element_name}"
273
+ end
274
+ fieldquery[0] = " "
275
+
276
+ # Paginate into (large) result sets staring with offset = 0 and page_sz = 100
277
+ offset, page_sz = 0, 100
278
+ loop do
279
+ querystr = "SELECT #{fieldquery} from #{crm_object} limit #{page_sz} offset #{offset}"
280
+ requesturl = @resturl + "/query/?q=" + CGI::escape(querystr)
281
+ raw_data = RestClient.get(requesturl, @restheaders)
282
+ parsed_data = JSON.parse raw_data
283
+
284
+ @result ||= {}
285
+ parsed_data["records"].each do |record|
286
+ record_hash = {}
287
+ @fields.each do |element_name, element_def|
288
+ record_hash[element_name] = record[element_name]
289
+ end
290
+ @result[record['Id']] = record_hash
291
+ end
292
+ stash_result # => @result is nil now
293
+ break if parsed_data['done']
294
+ offset += page_sz
295
+ end
296
+ end
297
+
298
+ `rhoconnect-adapters` gem provides reference implementation of paginating in `query` method for Sugar, Salesforce, and OracleOnDemand adapters.
232
299
 
233
300
  ## Running the CRM Application
234
301
  Once your Rhoconnect application is customized and ready to run, you can start it like any other Rhoconnect app.
@@ -240,4 +307,4 @@ Type the following command in the CRM application's root directory:
240
307
  ## Meta
241
308
  Created and maintained by Rhomobile Inc.
242
309
 
243
- Released under the [MIT License](http://www.opensource.org/licenses/mit-license.php).
310
+ Released under the [MIT License](http://www.opensource.org/licenses/mit-license.php).
@@ -43,7 +43,7 @@ module RhoconnectAdapters
43
43
  def get_object_settings
44
44
  return @object_settings if @object_settings
45
45
  begin
46
- @object_settings = RhoconnectAdapters::CRM::Field.load_file(File.join(ROOT_PATH,'vendor','ms_dynamics','settings',"#{crm_object.downcase}.yml"))
46
+ @object_settings = RhoconnectAdapters::CRM::Field.load_file(File.join(ROOT_PATH,'vendor','ms_dynamics','settings',"#{crm_object}.yml"))
47
47
  rescue Exception => e
48
48
  puts "Error opening CRMObjects settings file: #{e}"
49
49
  puts e.backtrace.join("\n")
@@ -89,14 +89,7 @@ module RhoconnectAdapters
89
89
  end
90
90
 
91
91
  def query(params=nil)
92
- # TODO: Query your backend data source and assign the records
93
- # to a nested hash structure called @result. For example:
94
- # @result = {
95
- # "1"=>{"name"=>"Acme", "industry"=>"Electronics"},
96
- # "2"=>{"name"=>"Best", "industry"=>"Software"}
97
- # }
98
- @result = {}
99
-
92
+ @result = {}
100
93
  attributes = []
101
94
  # strip out artificial 'attrtype' fields
102
95
  fields.each do |key, val|
@@ -298,4 +291,4 @@ module RhoconnectAdapters
298
291
  end
299
292
  end
300
293
  end
301
- end
294
+ end
@@ -10,13 +10,14 @@ describe "Application" do
10
10
 
11
11
  it "should authenticate" do
12
12
  auth_info = RhoconnectAdapters::CRM::MsDynamics.load_auth_info(@test_user)
13
+ auth_info.should_not be_nil
13
14
  now = DateTime.now
14
15
  should_be_between(auth_info['wlid_expires'],now,(now+(60 * 60 * 24)))
15
16
  should_be_between(auth_info['crm_ticket_expires'],now,(now+(60 * 60 * 24)))
16
17
  end
17
18
 
18
19
  it "should not authenticate with wrong credentials" do
19
- Application.should_receive(:warn).once.with('Can\'t authenticate user wrong_user: #<RuntimeError: RhoconnectAdapters::CRM::MsDynamics::WlidService error w/ IssueTicket: Authentication Failure (0x80041034): The specified member name is either invalid or empty.&#13;>')
20
+ Application.should_receive(:warn).once.with('Can\'t authenticate user wrong_user: #<RuntimeError: RhoconnectAdapters::CRM::MsDynamics::WlidService error w/ IssueTicket: Authentication Failure (0x80041034): The specified member name is either invalid or empty.&#xD;>')
20
21
  Application.authenticate('wrong_user','wrong_password',nil).should be_false
21
22
  end
22
23
  end
@@ -180,41 +180,65 @@ module RhoconnectAdapters
180
180
  end
181
181
 
182
182
  def query(params=nil)
183
- # TODO: Query your backend data source and assign the records
184
- # to a nested hash structure called @result. For example:
185
- # @result = {
186
- # "1"=>{"name"=>"Acme", "industry"=>"Electronics"},
187
- # "2"=>{"name"=>"Best", "industry"=>"Software"}
188
- # }
189
- @result = {}
183
+ #
184
+ # Straightforward way to query data. Dot not fit for large result sets.
185
+ #
186
+ # @result = {}
187
+ # fetch_more = 'true'
188
+ # start_row = 0
189
+ # begin
190
+ # soap_body = "<wsdl:ListOf#{crm_object} recordcountneeded=\"true\" pagesize=\"100\" startrownum=\"#{start_row.to_s}\">
191
+ # <wsdl:#{crm_object} searchspec=\"\">
192
+ # #{Adapter.get_columns(fields)}
193
+ # </wsdl:#{crm_object}>
194
+ # </wsdl:ListOf#{crm_object}>"
195
+ #
196
+ # query_results = execute_soap_action('QueryPage', soap_body)
197
+ # fetch_more = query_results['lastpage'] == 'true' ? false : true;
198
+ #
199
+ # query_results.children.each do |record|
200
+ # if record.name == "#{crm_object}"
201
+ # id_field = RhoconnectAdapters::SoapService.select_node_text(record, "#{crm_object}doc:Id")
202
+ # converted_record = {}
203
+ # # grab only the allowed fields
204
+ # fields.each do |element_name,element_def|
205
+ # converted_record[element_name] = RhoconnectAdapters::SoapService.select_node_text(record, "#{crm_object}doc:#{element_name}")
206
+ # end
207
+ # @result[id_field] = converted_record
208
+ # end
209
+ # end
210
+ # start_row = @result.size
211
+ # end while fetch_more
212
+ # @result
213
+
214
+ # Use stash_result instead of accumulating output in @result in every loop
190
215
  fetch_more = 'true'
191
216
  start_row = 0
192
217
  begin
193
-
194
218
  soap_body = "<wsdl:ListOf#{crm_object} recordcountneeded=\"true\" pagesize=\"100\" startrownum=\"#{start_row.to_s}\">
195
- <wsdl:#{crm_object} searchspec=\"\">
196
- #{Adapter.get_columns(fields)}
197
- </wsdl:#{crm_object}>
219
+ <wsdl:#{crm_object} searchspec=\"\">
220
+ #{Adapter.get_columns(fields)}
221
+ </wsdl:#{crm_object}>
198
222
  </wsdl:ListOf#{crm_object}>"
199
-
200
223
  query_results = execute_soap_action('QueryPage', soap_body)
201
224
  fetch_more = query_results['lastpage'] == 'true' ? false : true;
202
-
225
+
226
+ @result ||= {}
203
227
  query_results.children.each do |record|
204
228
  if record.name == "#{crm_object}"
205
- id_field = RhoconnectAdapters::SoapService.select_node_text(record, "#{crm_object}doc:Id")
206
- converted_record = {}
207
- # grab only the allowed fields
208
- fields.each do |element_name,element_def|
209
- converted_record[element_name] = RhoconnectAdapters::SoapService.select_node_text(record, "#{crm_object}doc:#{element_name}")
210
- end
211
- @result[id_field] = converted_record
212
- end
213
- end
214
- start_row = @result.size
215
- end while fetch_more
216
- @result
217
- end
229
+ id_field = RhoconnectAdapters::SoapService.select_node_text(record, "#{crm_object}doc:Id")
230
+ converted_record = {}
231
+ # grab only the allowed fields
232
+ fields.each do |element_name,element_def|
233
+ converted_record[element_name] = RhoconnectAdapters::SoapService.select_node_text(record, "#{crm_object}doc:#{element_name}")
234
+ end
235
+ @result[id_field] = converted_record
236
+ end
237
+ end
238
+ stash_result
239
+ start_row = @result.size
240
+ end while fetch_more
241
+ end
218
242
 
219
243
  def sync
220
244
  # Manipulate @result before it is saved, or save it
@@ -418,4 +442,4 @@ module RhoconnectAdapters
418
442
  end
419
443
  end
420
444
  end
421
- end
445
+ end
@@ -33,7 +33,7 @@ module RhoconnectAdapters
33
33
  def get_object_settings
34
34
  return @object_settings if @object_settings
35
35
  begin
36
- @object_settings = RhoconnectAdapters::CRM::Field.load_file(File.join(ROOT_PATH,'vendor','salesforce','settings',"#{crm_object.downcase}.yml"))
36
+ @object_settings = RhoconnectAdapters::CRM::Field.load_file(File.join(ROOT_PATH,'vendor','salesforce','settings',"#{crm_object}.yml"))
37
37
  rescue Exception => e
38
38
  puts "Error opening CRMObjects settings file: #{e}"
39
39
  puts e.backtrace.join("\n")
@@ -105,13 +105,33 @@ module RhoconnectAdapters
105
105
  end
106
106
 
107
107
  def query(params=nil)
108
- # TODO: Query your backend data source and assign the records
109
- # to a nested hash structure called @result. For example:
110
- # @result = {
111
- # "1"=>{"name"=>"Acme", "industry"=>"Electronics"},
112
- # "2"=>{"name"=>"Best", "industry"=>"Software"}
113
- # }
114
- @result = {}
108
+ #
109
+ # Straightforward way to query data. Dot not fit for large result sets.
110
+ #
111
+ # @result = {}
112
+ # fieldquery = ""
113
+ # @fields.each do |element_name, element_def|
114
+ # fieldquery << ",#{element_name}"
115
+ # end
116
+ # fieldquery[0] = " "
117
+ #
118
+ # querystr = "SELECT #{fieldquery} FROM #{crm_object}"
119
+ # requesturl = @resturl + "/query/?q=" + CGI::escape(querystr)
120
+ # raw_data = RestClient.get(requesturl, @restheaders)
121
+ # parsed_data = JSON.parse raw_data
122
+ #
123
+ # if parsed_data['done']
124
+ # parsed_data["records"].each do |record|
125
+ # record_hash = {}
126
+ # @fields.each do |element_name, element_def|
127
+ # record_hash[element_name] = record[element_name]
128
+ # end
129
+ # @result[record['Id']] = record_hash
130
+ # end
131
+ # else
132
+ # # TODO: queryMore
133
+ # end
134
+ # @result
115
135
 
116
136
  fieldquery = ""
117
137
  @fields.each do |element_name, element_def|
@@ -119,23 +139,28 @@ module RhoconnectAdapters
119
139
  end
120
140
  fieldquery[0] = " "
121
141
 
122
- querystr = "SELECT #{fieldquery} from #{crm_object}"
142
+ # Paginate into (large) result sets staring with offset = 0 and page_sz = 100
143
+ offset, page_sz = 0, 100
144
+ loop do
145
+ querystr = "SELECT #{fieldquery} from #{crm_object} limit #{page_sz} offset #{offset}"
146
+ requesturl = @resturl + "/query/?q=" + CGI::escape(querystr)
147
+ raw_data = RestClient.get(requesturl, @restheaders)
148
+ parsed_data = JSON.parse raw_data
123
149
 
124
- requesturl = @resturl + "/query/?q=" + CGI::escape(querystr)
125
- raw_data = RestClient.get(requesturl, @restheaders)
126
- parsed_data = JSON.parse raw_data
127
-
128
- parsed_data["records"].each do |record|
129
- record_hash = {}
130
- @fields.each do |element_name, element_def|
131
- record_hash[element_name] = record[element_name]
150
+ @result ||= {}
151
+ parsed_data["records"].each do |record|
152
+ record_hash = {}
153
+ @fields.each do |element_name, element_def|
154
+ record_hash[element_name] = record[element_name]
155
+ end
156
+ @result[record['Id']] = record_hash
132
157
  end
133
- @result[record['Id']] = record_hash
158
+ stash_result # => @result is nil now
159
+ break if parsed_data['done']
160
+ offset += page_sz
134
161
  end
135
-
136
- @result
137
162
  end
138
-
163
+
139
164
  def metadata
140
165
  # define the metadata
141
166
  show_fields = []
@@ -316,4 +341,4 @@ module RhoconnectAdapters
316
341
  end
317
342
  end
318
343
  end
319
- end
344
+ end
@@ -29,7 +29,7 @@ module RhoconnectAdapters
29
29
  # here, requestUrl is formatted for SOAP requests
30
30
  # and we removing Soap part for it (which is suffix after services)
31
31
  # since we will user REST after that
32
- endpoint_url = res_hash['serverUrl'].split('services')[0] + 'services/data/v22.0'
32
+ endpoint_url = res_hash['serverUrl'].split('services')[0] + 'services/data/v24.0'
33
33
 
34
34
  # store password to be used by SourceAdaptors
35
35
  Store.put_value("#{username}:session_id", session_id)
@@ -104,12 +104,31 @@ module RhoconnectAdapters
104
104
  end
105
105
 
106
106
  def query(params=nil)
107
- @result = {}
107
+ #
108
+ # Straightforward way to query data. Dot not fit for large result sets.
109
+ #
110
+ # @result = {}
111
+ # conditions = {:conditions=>{}}
112
+ # conditions[:conditions][:assigned_user_id] = @namespace.current_user.id
113
+ # results = get_results(conditions)
114
+ # @result = create_result_hash(results)
115
+
116
+ # Use stash_result to paginate into result sets
108
117
  conditions = {:conditions=>{}}
109
118
  conditions[:conditions][:assigned_user_id] = @namespace.current_user.id
110
- results = get_results(conditions)
111
-
112
- @result = create_result_hash(results)
119
+ offset, page_sz = 0, 100
120
+ conditions[:limit] = page_sz
121
+
122
+ loop do
123
+ conditions[:offset] = offset
124
+ results = get_results(conditions)
125
+ @result = create_result_hash(results)
126
+ rec_num = @result.size
127
+ stash_result
128
+
129
+ break if rec_num < page_sz
130
+ offset += page_sz
131
+ end
113
132
  end
114
133
 
115
134
  def metadata
@@ -1,3 +1,3 @@
1
1
  module RhoconnectAdapters
2
- VERSION = "1.0.5"
2
+ VERSION = "1.0.6"
3
3
  end
@@ -27,10 +27,10 @@ Gem::Specification.new do |s|
27
27
  ]
28
28
 
29
29
  s.add_dependency('bundler', '~> 1.0')
30
- s.add_dependency('rhoconnect', '>= 3.2')
30
+ s.add_dependency('rhoconnect', '>= 3.4')
31
31
  s.add_dependency('activesupport', '>= 3.0.9')
32
32
  s.add_dependency('i18n', '>= 0.6.0')
33
- s.add_dependency('rake', '~> 0.9.2.2')
34
- s.add_dependency('nokogiri', '>= 1.4.6')
33
+ s.add_dependency('rake', '~> 10.0')
34
+ s.add_dependency('nokogiri', '~> 1.5')
35
35
  s.add_dependency('templater', '~> 1.0.0')
36
36
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rhoconnect-adapters
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.5
4
+ version: 1.0.6
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2012-07-19 00:00:00.000000000 Z
12
+ date: 2012-12-14 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: bundler
@@ -34,7 +34,7 @@ dependencies:
34
34
  requirements:
35
35
  - - ! '>='
36
36
  - !ruby/object:Gem::Version
37
- version: '3.2'
37
+ version: '3.4'
38
38
  type: :runtime
39
39
  prerelease: false
40
40
  version_requirements: !ruby/object:Gem::Requirement
@@ -42,7 +42,7 @@ dependencies:
42
42
  requirements:
43
43
  - - ! '>='
44
44
  - !ruby/object:Gem::Version
45
- version: '3.2'
45
+ version: '3.4'
46
46
  - !ruby/object:Gem::Dependency
47
47
  name: activesupport
48
48
  requirement: !ruby/object:Gem::Requirement
@@ -82,7 +82,7 @@ dependencies:
82
82
  requirements:
83
83
  - - ~>
84
84
  - !ruby/object:Gem::Version
85
- version: 0.9.2.2
85
+ version: '10.0'
86
86
  type: :runtime
87
87
  prerelease: false
88
88
  version_requirements: !ruby/object:Gem::Requirement
@@ -90,23 +90,23 @@ dependencies:
90
90
  requirements:
91
91
  - - ~>
92
92
  - !ruby/object:Gem::Version
93
- version: 0.9.2.2
93
+ version: '10.0'
94
94
  - !ruby/object:Gem::Dependency
95
95
  name: nokogiri
96
96
  requirement: !ruby/object:Gem::Requirement
97
97
  none: false
98
98
  requirements:
99
- - - ! '>='
99
+ - - ~>
100
100
  - !ruby/object:Gem::Version
101
- version: 1.4.6
101
+ version: '1.5'
102
102
  type: :runtime
103
103
  prerelease: false
104
104
  version_requirements: !ruby/object:Gem::Requirement
105
105
  none: false
106
106
  requirements:
107
- - - ! '>='
107
+ - - ~>
108
108
  - !ruby/object:Gem::Version
109
- version: 1.4.6
109
+ version: '1.5'
110
110
  - !ruby/object:Gem::Dependency
111
111
  name: templater
112
112
  requirement: !ruby/object:Gem::Requirement
@@ -250,7 +250,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
250
250
  version: '0'
251
251
  segments:
252
252
  - 0
253
- hash: 2546656225608389328
253
+ hash: 1998545148818093268
254
254
  required_rubygems_version: !ruby/object:Gem::Requirement
255
255
  none: false
256
256
  requirements:
@@ -259,7 +259,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
259
259
  version: '0'
260
260
  requirements: []
261
261
  rubyforge_project:
262
- rubygems_version: 1.8.24
262
+ rubygems_version: 1.8.23
263
263
  signing_key:
264
264
  specification_version: 3
265
265
  summary: Rhoconnect adapters