rhoconnect-adapters 1.0.5 → 1.0.6

Sign up to get free protection for your applications and to get access to all the features.
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