rhoconnect-adapters 1.0.0.beta1
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 +4 -0
- data/CHANGELOG +4 -0
- data/Gemfile +18 -0
- data/README.md +239 -0
- data/Rakefile +31 -0
- data/bin/rhoconnect-adapters +35 -0
- data/generators/crm/templates/application/application.rb +34 -0
- data/generators/crm/templates/source/source_adapter.rb +10 -0
- data/generators/crm/templates/source/source_spec.rb +25 -0
- data/generators/crm/templates/spec/spec_helper.rb +64 -0
- data/generators/crm/vendor/ms_dynamics/adapter.rb +301 -0
- data/generators/crm/vendor/ms_dynamics/application.rb +56 -0
- data/generators/crm/vendor/ms_dynamics/lib/crm_metadata_service.rb +43 -0
- data/generators/crm/vendor/ms_dynamics/lib/crm_service.rb +141 -0
- data/generators/crm/vendor/ms_dynamics/lib/discovery_service.rb +51 -0
- data/generators/crm/vendor/ms_dynamics/lib/wlid_service.rb +159 -0
- data/generators/crm/vendor/ms_dynamics/ms_dynamics.rb +45 -0
- data/generators/crm/vendor/ms_dynamics/settings/Account.yml +46 -0
- data/generators/crm/vendor/ms_dynamics/settings/Contact.yml +40 -0
- data/generators/crm/vendor/ms_dynamics/settings/GenericObject.yml +18 -0
- data/generators/crm/vendor/ms_dynamics/settings/Lead.yml +53 -0
- data/generators/crm/vendor/ms_dynamics/settings/Opportunity.yml +41 -0
- data/generators/crm/vendor/ms_dynamics/settings/settings.yml +9 -0
- data/generators/crm/vendor/ms_dynamics/spec/application_spec.rb +23 -0
- data/generators/crm/vendor/ms_dynamics/spec/sources/account_spec.rb +49 -0
- data/generators/crm/vendor/ms_dynamics/spec/sources/contact_spec.rb +49 -0
- data/generators/crm/vendor/ms_dynamics/spec/sources/lead_spec.rb +49 -0
- data/generators/crm/vendor/ms_dynamics/spec/sources/opportunity_spec.rb +62 -0
- data/generators/crm/vendor/ms_dynamics/spec/spec_helper.rb +78 -0
- data/generators/crm/vendor/ms_dynamics/spec_data/Account.yml +6 -0
- data/generators/crm/vendor/ms_dynamics/spec_data/Contact.yml +7 -0
- data/generators/crm/vendor/ms_dynamics/spec_data/GenericObject.yml +3 -0
- data/generators/crm/vendor/ms_dynamics/spec_data/Lead.yml +12 -0
- data/generators/crm/vendor/ms_dynamics/spec_data/Opportunity.yml +6 -0
- data/generators/crm/vendor/ms_dynamics/templates.rb +57 -0
- data/generators/crm/vendor/oracle_on_demand/adapter.rb +421 -0
- data/generators/crm/vendor/oracle_on_demand/application.rb +64 -0
- data/generators/crm/vendor/oracle_on_demand/settings/Account.yml +91 -0
- data/generators/crm/vendor/oracle_on_demand/settings/Contact.yml +54 -0
- data/generators/crm/vendor/oracle_on_demand/settings/GenericObject.yml +21 -0
- data/generators/crm/vendor/oracle_on_demand/settings/Lead.yml +72 -0
- data/generators/crm/vendor/oracle_on_demand/settings/Opportunity.yml +69 -0
- data/generators/crm/vendor/oracle_on_demand/settings/settings.yml +8 -0
- data/generators/crm/vendor/oracle_on_demand/spec/application_spec.rb +14 -0
- data/generators/crm/vendor/oracle_on_demand/spec/sources/account_spec.rb +50 -0
- data/generators/crm/vendor/oracle_on_demand/spec/sources/contact_spec.rb +50 -0
- data/generators/crm/vendor/oracle_on_demand/spec/sources/lead_spec.rb +51 -0
- data/generators/crm/vendor/oracle_on_demand/spec/sources/opportunity_spec.rb +51 -0
- data/generators/crm/vendor/oracle_on_demand/spec_data/Account.yml +8 -0
- data/generators/crm/vendor/oracle_on_demand/spec_data/Contact.yml +8 -0
- data/generators/crm/vendor/oracle_on_demand/spec_data/GenericObject.yml +4 -0
- data/generators/crm/vendor/oracle_on_demand/spec_data/Lead.yml +14 -0
- data/generators/crm/vendor/oracle_on_demand/spec_data/Opportunity.yml +6 -0
- data/generators/crm/vendor/oracle_on_demand/templates.rb +52 -0
- data/generators/crm/vendor/salesforce/adapter.rb +315 -0
- data/generators/crm/vendor/salesforce/application.rb +80 -0
- data/generators/crm/vendor/salesforce/settings/Account.yml +53 -0
- data/generators/crm/vendor/salesforce/settings/Contact.yml +61 -0
- data/generators/crm/vendor/salesforce/settings/GenericObject.yml +13 -0
- data/generators/crm/vendor/salesforce/settings/Lead.yml +73 -0
- data/generators/crm/vendor/salesforce/settings/Opportunity.yml +48 -0
- data/generators/crm/vendor/salesforce/settings/settings.yml +6 -0
- data/generators/crm/vendor/salesforce/spec/application_spec.rb +14 -0
- data/generators/crm/vendor/salesforce/spec/sources/account_spec.rb +50 -0
- data/generators/crm/vendor/salesforce/spec/sources/contact_spec.rb +50 -0
- data/generators/crm/vendor/salesforce/spec/sources/lead_spec.rb +51 -0
- data/generators/crm/vendor/salesforce/spec/sources/opportunity_spec.rb +51 -0
- data/generators/crm/vendor/salesforce/spec_data/Account.yml +14 -0
- data/generators/crm/vendor/salesforce/spec_data/Contact.yml +8 -0
- data/generators/crm/vendor/salesforce/spec_data/GenericObject.yml +3 -0
- data/generators/crm/vendor/salesforce/spec_data/Lead.yml +10 -0
- data/generators/crm/vendor/salesforce/spec_data/Opportunity.yml +10 -0
- data/generators/crm/vendor/salesforce/templates.rb +45 -0
- data/generators/crm/vendor/sugar/adapter.rb +291 -0
- data/generators/crm/vendor/sugar/application.rb +50 -0
- data/generators/crm/vendor/sugar/settings/Account.yml +49 -0
- data/generators/crm/vendor/sugar/settings/Contact.yml +62 -0
- data/generators/crm/vendor/sugar/settings/GenericObject.yml +12 -0
- data/generators/crm/vendor/sugar/settings/Lead.yml +76 -0
- data/generators/crm/vendor/sugar/settings/Opportunity.yml +49 -0
- data/generators/crm/vendor/sugar/settings/settings.yml +9 -0
- data/generators/crm/vendor/sugar/spec/application_spec.rb +25 -0
- data/generators/crm/vendor/sugar/spec/sources/account_spec.rb +53 -0
- data/generators/crm/vendor/sugar/spec/sources/contact_spec.rb +53 -0
- data/generators/crm/vendor/sugar/spec/sources/lead_spec.rb +54 -0
- data/generators/crm/vendor/sugar/spec/sources/opportunity_spec.rb +54 -0
- data/generators/crm/vendor/sugar/spec_data/Account.yml +13 -0
- data/generators/crm/vendor/sugar/spec_data/Contact.yml +8 -0
- data/generators/crm/vendor/sugar/spec_data/GenericObject.yml +3 -0
- data/generators/crm/vendor/sugar/spec_data/Lead.yml +16 -0
- data/generators/crm/vendor/sugar/spec_data/Opportunity.yml +10 -0
- data/generators/crm/vendor/sugar/sugar.rb +33 -0
- data/generators/crm/vendor/sugar/templates.rb +58 -0
- data/generators/rhoconnect-adapters.rb +217 -0
- data/lib/rhoconnect-adapters/crm/crm.rb +31 -0
- data/lib/rhoconnect-adapters/soap_service.rb +70 -0
- data/lib/rhoconnect-adapters/version.rb +3 -0
- data/lib/rhoconnect-adapters.rb +2 -0
- data/rhoconnect-adapters.gemspec +36 -0
- data/spec/apps/ms_dynamics_spec.rb +19 -0
- data/spec/apps/oracle_on_demand_spec.rb +20 -0
- data/spec/apps/salesforce_spec.rb +18 -0
- data/spec/apps/sugar_spec.rb +18 -0
- data/spec/generator/generator_spec.rb +113 -0
- data/spec/spec_helper.rb +57 -0
- metadata +288 -0
|
@@ -0,0 +1,315 @@
|
|
|
1
|
+
require 'rhoconnect-adapters'
|
|
2
|
+
require 'rest-client'
|
|
3
|
+
|
|
4
|
+
module RhoconnectAdapters
|
|
5
|
+
module CRM
|
|
6
|
+
module Salesforce
|
|
7
|
+
class Adapter < SourceAdapter
|
|
8
|
+
attr_accessor :crm_object
|
|
9
|
+
attr_accessor :fields
|
|
10
|
+
|
|
11
|
+
def initialize(source)
|
|
12
|
+
super(source)
|
|
13
|
+
@crm_object = self.class.name
|
|
14
|
+
@fields = {}
|
|
15
|
+
@title_fields = ["#{crm_object.downcase}id"]
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def configure_fields
|
|
19
|
+
# initialize fields map
|
|
20
|
+
@fields = get_object_settings['Query_Fields']
|
|
21
|
+
@field_picklists = {}
|
|
22
|
+
|
|
23
|
+
@object_fields = get_object_settings['ObjectFields']
|
|
24
|
+
@object_fields = {} if @object_fields == nil
|
|
25
|
+
|
|
26
|
+
# title fields are used in metadata to show
|
|
27
|
+
# records in the list
|
|
28
|
+
@title_fields = get_object_settings['TitleFields']
|
|
29
|
+
|
|
30
|
+
@fields
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def get_object_settings
|
|
34
|
+
return @object_settings if @object_settings
|
|
35
|
+
begin
|
|
36
|
+
@object_settings = RhoconnectAdapters::CRM::Field.load_file(File.join(ROOT_PATH,'vendor','salesforce','settings',"#{crm_object.downcase}.yml"))
|
|
37
|
+
rescue Exception => e
|
|
38
|
+
puts "Error opening CRMObjects settings file: #{e}"
|
|
39
|
+
puts e.backtrace.join("\n")
|
|
40
|
+
raise e
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def get_picklists
|
|
45
|
+
begin
|
|
46
|
+
already_described = Store.get_value("#{crm_object}:already_described")
|
|
47
|
+
return if already_described
|
|
48
|
+
|
|
49
|
+
# call describe method to retrieve the object's metadata
|
|
50
|
+
request_url = "#{@resturl}/sobjects/#{crm_object}/describe/"
|
|
51
|
+
parsed = JSON.parse(RestClient.get(request_url, @restheaders).body)
|
|
52
|
+
parsed["fields"].each do |field|
|
|
53
|
+
element_name = field['name']
|
|
54
|
+
|
|
55
|
+
#puts " we have field #{element_name}"
|
|
56
|
+
#puts " picklist values are : #{field["picklistValues"].inspect}"
|
|
57
|
+
|
|
58
|
+
next unless fields[element_name] != nil
|
|
59
|
+
data_type = fields[element_name]['Type']
|
|
60
|
+
if data_type == 'Picklist' and not @field_picklists.has_key?(element_name)
|
|
61
|
+
@field_picklists[element_name] = get_picklist(element_name, field)
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
Store.put_value("#{crm_object}:already_described", true)
|
|
65
|
+
rescue RestClient::Exception => e
|
|
66
|
+
raise e
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
def get_picklist(element_name, field_data)
|
|
71
|
+
# check if we already have it in Store
|
|
72
|
+
picklist = Store.get_data("#{crm_object}:#{element_name}_picklist",Array)
|
|
73
|
+
return picklist if picklist.size != 0
|
|
74
|
+
|
|
75
|
+
field_values = []
|
|
76
|
+
field_data["picklistValues"].each do |v|
|
|
77
|
+
field_values << v['value']
|
|
78
|
+
end
|
|
79
|
+
Store.put_data("#{crm_object}:#{element_name}_picklist", field_values)
|
|
80
|
+
field_values
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
def login
|
|
84
|
+
@session_id = Store.get_value("#{current_user.login}:session_id")
|
|
85
|
+
@resturl = Store.get_value("#{current_user.login}:service_url")
|
|
86
|
+
@restheaders = {
|
|
87
|
+
"Accept" => "*/*",
|
|
88
|
+
"Authorization" => "OAuth #{@session_id}",
|
|
89
|
+
"X-PrettyPrint" => "1"
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
@postheaders = {
|
|
93
|
+
"Accept" => "*/*",
|
|
94
|
+
"Content-Type" => "application/json",
|
|
95
|
+
"Authorization" => "OAuth #{@session_id}",
|
|
96
|
+
"X-PrettyPrint" => "1"
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
# query picklist values
|
|
100
|
+
get_picklists
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
def query(params=nil)
|
|
104
|
+
# TODO: Query your backend data source and assign the records
|
|
105
|
+
# to a nested hash structure called @result. For example:
|
|
106
|
+
# @result = {
|
|
107
|
+
# "1"=>{"name"=>"Acme", "industry"=>"Electronics"},
|
|
108
|
+
# "2"=>{"name"=>"Best", "industry"=>"Software"}
|
|
109
|
+
# }
|
|
110
|
+
@result = {}
|
|
111
|
+
|
|
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
|
+
|
|
120
|
+
requesturl = @resturl + "/query/?q=" + CGI::escape(querystr)
|
|
121
|
+
raw_data = RestClient.get(requesturl, @restheaders)
|
|
122
|
+
parsed_data = JSON.parse raw_data
|
|
123
|
+
|
|
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
|
+
|
|
132
|
+
@result
|
|
133
|
+
end
|
|
134
|
+
|
|
135
|
+
def metadata
|
|
136
|
+
# define the metadata
|
|
137
|
+
show_fields = []
|
|
138
|
+
new_fields = []
|
|
139
|
+
edit_fields = []
|
|
140
|
+
model_name = "" + crm_object
|
|
141
|
+
model_name[0] = model_name[0,1].downcase
|
|
142
|
+
record_sym = '@' + "#{model_name}"
|
|
143
|
+
|
|
144
|
+
fields.each do |element_name,element_def|
|
|
145
|
+
next if element_name == 'Id'
|
|
146
|
+
|
|
147
|
+
# 1) - read-only show fields
|
|
148
|
+
field_type = 'labeledvalueli'
|
|
149
|
+
field = {
|
|
150
|
+
:name => "#{model_name}\[#{element_name}\]",
|
|
151
|
+
:label => element_def['Label'],
|
|
152
|
+
:type => field_type,
|
|
153
|
+
:value => "{{#{record_sym}/#{element_name}}}"
|
|
154
|
+
}
|
|
155
|
+
show_fields << field
|
|
156
|
+
|
|
157
|
+
new_field = field.clone
|
|
158
|
+
new_field[:type] = 'labeledinputli'
|
|
159
|
+
new_field.delete(:value)
|
|
160
|
+
case element_def['Type']
|
|
161
|
+
when 'Picklist'
|
|
162
|
+
new_field[:type] = 'select'
|
|
163
|
+
values = []
|
|
164
|
+
# make first element a blank value
|
|
165
|
+
values[0] = nil
|
|
166
|
+
values.concat @field_picklists[element_name]
|
|
167
|
+
new_field[:values] = values
|
|
168
|
+
new_field[:value] = values[0]
|
|
169
|
+
when 'object'
|
|
170
|
+
end
|
|
171
|
+
|
|
172
|
+
new_fields << new_field if not element_def['Type'] == 'object'
|
|
173
|
+
|
|
174
|
+
edit_field = new_field.clone
|
|
175
|
+
edit_field[:value] = "{{#{record_sym}/#{element_name}}}"
|
|
176
|
+
edit_fields << edit_field
|
|
177
|
+
end
|
|
178
|
+
|
|
179
|
+
# Show
|
|
180
|
+
show_list = { :name => 'list', :type => 'list', :children => show_fields }
|
|
181
|
+
show_form = {
|
|
182
|
+
:name => "#{crm_object}_show",
|
|
183
|
+
:type => 'show_form',
|
|
184
|
+
:title => "#{crm_object} details",
|
|
185
|
+
:object => "#{crm_object}",
|
|
186
|
+
:model => "#{model_name}",
|
|
187
|
+
:id => "{{#{record_sym}/object}}",
|
|
188
|
+
:children => [show_list]
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
# New
|
|
192
|
+
new_list = show_list.clone
|
|
193
|
+
new_list[:children] = new_fields
|
|
194
|
+
new_form = {
|
|
195
|
+
:type => 'new_form',
|
|
196
|
+
:title => "New #{crm_object}",
|
|
197
|
+
:object => "#{crm_object}",
|
|
198
|
+
:model => "#{model_name}",
|
|
199
|
+
:children => [new_list]
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
# Edit
|
|
203
|
+
edit_list = show_list.clone
|
|
204
|
+
edit_list[:children] = edit_fields
|
|
205
|
+
edit_form = {
|
|
206
|
+
:type => 'update_form',
|
|
207
|
+
:title => "Edit #{crm_object}",
|
|
208
|
+
:object => "#{crm_object}",
|
|
209
|
+
:model => "#{model_name}",
|
|
210
|
+
:id => "{{#{record_sym}/object}}",
|
|
211
|
+
:children => [edit_list]
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
# Index
|
|
215
|
+
title_field_metadata = @title_fields.collect { |field_name | "{{#{field_name.to_s}}} " }.join(' ')
|
|
216
|
+
object_rec = {
|
|
217
|
+
:object => "#{crm_object}",
|
|
218
|
+
:id => "{{object}}",
|
|
219
|
+
:type => 'linkobj',
|
|
220
|
+
:text => "#{title_field_metadata}"
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
index_form = {
|
|
224
|
+
:object => "#{crm_object}",
|
|
225
|
+
:title => "#{crm_object.pluralize}",
|
|
226
|
+
:type => 'index_form',
|
|
227
|
+
:children => [object_rec],
|
|
228
|
+
:repeatable => "{{#{record_sym.pluralize}}}"
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
# return JSON
|
|
232
|
+
{ 'index' => index_form, 'show' => show_form, 'new' => new_form, 'edit' => edit_form }.to_json
|
|
233
|
+
end
|
|
234
|
+
|
|
235
|
+
def create(create_hash,blob=nil)
|
|
236
|
+
# TODO: Create a new record in your backend data source
|
|
237
|
+
# If your rhodes rhom object contains image/binary data
|
|
238
|
+
# (has the image_uri attribute), then a blob will be provided
|
|
239
|
+
created_object_id = nil
|
|
240
|
+
request_fields = {}
|
|
241
|
+
field_types = {}
|
|
242
|
+
fields.each do |element_name, element_def|
|
|
243
|
+
field_value = create_hash[element_name]
|
|
244
|
+
next unless (element_name != "Id" and field_value != nil)
|
|
245
|
+
|
|
246
|
+
# special case for Datetime types - they need to
|
|
247
|
+
# be converted to W3C XML trailing Z
|
|
248
|
+
if element_def['Type'] == 'datetime'
|
|
249
|
+
field_value = Date.parse(field_value).strftime '%Y-%m-%dT%H:%M:%S.000Z'
|
|
250
|
+
end
|
|
251
|
+
request_fields[element_name] = field_value
|
|
252
|
+
end
|
|
253
|
+
|
|
254
|
+
begin
|
|
255
|
+
requesturl = "#{@resturl}/sobjects/#{crm_object}/"
|
|
256
|
+
raw_data = RestClient.post(requesturl, request_fields.to_json, @postheaders) do |response,request, result, &block|
|
|
257
|
+
case response.code
|
|
258
|
+
when 400
|
|
259
|
+
raise response.body
|
|
260
|
+
end
|
|
261
|
+
response.body
|
|
262
|
+
end
|
|
263
|
+
parsed = JSON.parse raw_data
|
|
264
|
+
created_object_id = parsed['id']
|
|
265
|
+
rescue Exception => e
|
|
266
|
+
raise e
|
|
267
|
+
end
|
|
268
|
+
created_object_id
|
|
269
|
+
end
|
|
270
|
+
|
|
271
|
+
def update(update_hash)
|
|
272
|
+
# it may be there as 'id' field
|
|
273
|
+
updated_object_id = update_hash['Id'] || update_hash['id']
|
|
274
|
+
if updated_object_id == nil
|
|
275
|
+
raise SourceAdapterObjectConflictError.new("Either 'Id' or 'id' field must be specified for the Update request")
|
|
276
|
+
end
|
|
277
|
+
|
|
278
|
+
request_fields = {}
|
|
279
|
+
field_types = {}
|
|
280
|
+
fields.each do |element_name,element_def|
|
|
281
|
+
next if (element_name == "Id" or element_name == 'id')
|
|
282
|
+
|
|
283
|
+
field_value = update_hash[element_name]
|
|
284
|
+
next unless field_value != nil
|
|
285
|
+
# special case for Datetime types - they need to
|
|
286
|
+
# be converted to W3C XML trailing Z
|
|
287
|
+
if element_def['Type'] == 'datetime'
|
|
288
|
+
field_value = Date.parse(field_value).strftime '%Y-%m-%dT%H:%M:%S.000Z'
|
|
289
|
+
end
|
|
290
|
+
request_fields[element_name] = field_value
|
|
291
|
+
end
|
|
292
|
+
|
|
293
|
+
requesturl = @resturl + "/sobjects/#{crm_object}/#{updated_object_id}?_HttpMethod=PATCH"
|
|
294
|
+
RestClient.post(requesturl, request_fields.to_json, @postheaders)
|
|
295
|
+
updated_object_id
|
|
296
|
+
end
|
|
297
|
+
|
|
298
|
+
def delete(delete_hash)
|
|
299
|
+
deleted_object_id = delete_hash["Id"] || delete_hash['id']
|
|
300
|
+
if deleted_object_id == nil
|
|
301
|
+
raise SourceAdapterObjectConflictError.new("Either 'Id' or 'id' field must be specified for the Delete request")
|
|
302
|
+
end
|
|
303
|
+
|
|
304
|
+
requesturl = @resturl + "/sobjects/#{crm_object}/#{deleted_object_id}?_HttpMethod=DELETE"
|
|
305
|
+
RestClient.post(requesturl, "", @postheaders)
|
|
306
|
+
deleted_object_id
|
|
307
|
+
end
|
|
308
|
+
|
|
309
|
+
def logoff
|
|
310
|
+
# logoff if necessary
|
|
311
|
+
end
|
|
312
|
+
end
|
|
313
|
+
end
|
|
314
|
+
end
|
|
315
|
+
end
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
require 'rhoconnect-adapters'
|
|
2
|
+
require 'rhoconnect-adapters/soap_service'
|
|
3
|
+
require 'active_support/core_ext'
|
|
4
|
+
|
|
5
|
+
module RhoconnectAdapters
|
|
6
|
+
module CRM
|
|
7
|
+
module Salesforce
|
|
8
|
+
RhoconnectAdapters::SoapService.envelope_namespaces += <<-DESC
|
|
9
|
+
xmlns:s=\"http://schemas.xmlsoap.org/soap/envelope/\"
|
|
10
|
+
DESC
|
|
11
|
+
|
|
12
|
+
class Application < Rhoconnect::Base
|
|
13
|
+
class << self
|
|
14
|
+
def authenticate(username,password,session=nil)
|
|
15
|
+
begin
|
|
16
|
+
salesforce_login_url = Application.get_settings[:salesforce_login_url]
|
|
17
|
+
|
|
18
|
+
request_body = "<login xmlns=\"urn:enterprise.soap.sforce.com\">
|
|
19
|
+
<username>#{username}</username>
|
|
20
|
+
<password>#{password}</password>
|
|
21
|
+
</login>"
|
|
22
|
+
|
|
23
|
+
soap_message = RhoconnectAdapters::SoapService.compose_message(nil, request_body)
|
|
24
|
+
response = RhoconnectAdapters::SoapService.send_request_raw(salesforce_login_url, soap_message, '""')
|
|
25
|
+
res_hash = Hash.from_xml(response)['Envelope']['Body']['loginResponse']['result']
|
|
26
|
+
|
|
27
|
+
# obtain session id and endpoint url
|
|
28
|
+
session_id = res_hash['sessionId'].split('!')[1]
|
|
29
|
+
# here, requestUrl is formatted for SOAP requests
|
|
30
|
+
# and we removing Soap part for it (which is suffix after services)
|
|
31
|
+
# since we will user REST after that
|
|
32
|
+
endpoint_url = res_hash['serverUrl'].split('services')[0] + 'services/data/v22.0'
|
|
33
|
+
|
|
34
|
+
# store password to be used by SourceAdaptors
|
|
35
|
+
Store.put_value("#{username}:session_id", session_id)
|
|
36
|
+
Store.put_value("#{username}:service_url", endpoint_url)
|
|
37
|
+
rescue Exception => e
|
|
38
|
+
warn "Can't authenticate user #{username}: " + e.inspect
|
|
39
|
+
return false
|
|
40
|
+
end
|
|
41
|
+
true
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def get_settings
|
|
45
|
+
return @settings if @settings
|
|
46
|
+
begin
|
|
47
|
+
file = YAML.load_file(File.join(ROOT_PATH,'settings','settings.yml'))
|
|
48
|
+
env = (ENV['RHO_ENV'] || :development).to_sym
|
|
49
|
+
@settings = file[env]
|
|
50
|
+
|
|
51
|
+
# vendor-specific settings
|
|
52
|
+
file = YAML.load_file(File.join(ROOT_PATH,'vendor','salesforce','settings','settings.yml'))
|
|
53
|
+
@settings.merge!(file[env])
|
|
54
|
+
rescue Exception => e
|
|
55
|
+
puts "Error opening settings file: #{e}"
|
|
56
|
+
puts e.backtrace.join("\n")
|
|
57
|
+
raise e
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
{"result"=>{"metadataServerUrl"=>"https://na3-api.salesforce.com/services/Soap/m/22.0/00D50000000Iyfx",
|
|
68
|
+
"passwordExpired"=>"false",
|
|
69
|
+
"sandbox"=>"false",
|
|
70
|
+
"serverUrl"=>"https://na3-api.salesforce.com/services/Soap/c/22.0/00D50000000Iyfx",
|
|
71
|
+
"sessionId"=>"00D50000000Iyfx!ARwAQHbIfRD2GDMENzerpqNEYhbArnxFZjjfdHJAakSRy0pfqjqAZfwxCaBlUB8b2kQ0UQlKlUOi1F9isT5BUEczvvFnXq4r",
|
|
72
|
+
"userId"=>"00550000001J8xJAAS",
|
|
73
|
+
"userInfo"=>{"accessibilityMode"=>"false", "currencySymbol"=>"$", "orgAttachmentFileSizeLimit"=>"5242880",
|
|
74
|
+
"orgDefaultCurrencyIsoCode"=>"USD", "orgDisallowHtmlAttachments"=>"false", "orgHasPersonAccounts"=>"false",
|
|
75
|
+
"organizationId"=>"00D50000000IyfxEAC", "organizationMultiCurrency"=>"false", "organizationName"=>"Rhomobile", "profileId"=>"00e50000001876vAAA",
|
|
76
|
+
"roleId"=>{"xsi:nil"=>"true"}, "sessionSecondsValid"=>"7200", "userDefaultCurrencyIsoCode"=>{"xsi:nil"=>"true"},
|
|
77
|
+
"userEmail"=>"brian@rhomobile.com", "userFullName"=>"Brian Moore",
|
|
78
|
+
"userId"=>"00550000001J8xJAAS", "userLanguage"=>"en_US", "userLocale"=>"en_US", "userName"=>"brian@rhomobile.com",
|
|
79
|
+
"userTimeZone"=>"America/Los_Angeles", "userType"=>"Standard", "userUiSkin"=>"Theme3"}}}
|
|
80
|
+
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
Query_Fields:
|
|
2
|
+
# This is just a sample list of fields
|
|
3
|
+
# that are used in Query request
|
|
4
|
+
Id:
|
|
5
|
+
Label: Id
|
|
6
|
+
Type: id
|
|
7
|
+
Name:
|
|
8
|
+
Label: Account Name
|
|
9
|
+
Type: textinput
|
|
10
|
+
Type:
|
|
11
|
+
Label: Account Type
|
|
12
|
+
Type: Picklist
|
|
13
|
+
Industry:
|
|
14
|
+
Label: Industry
|
|
15
|
+
Type: Picklist
|
|
16
|
+
Ownership:
|
|
17
|
+
Label: Ownership
|
|
18
|
+
Type: Picklist
|
|
19
|
+
AnnualRevenue:
|
|
20
|
+
Label: Annual Revenue
|
|
21
|
+
Type: textinput
|
|
22
|
+
NumberOfEmployees:
|
|
23
|
+
Label: Number Of Employees
|
|
24
|
+
Type: textinput
|
|
25
|
+
Phone:
|
|
26
|
+
Label: Phone
|
|
27
|
+
Type: textinput
|
|
28
|
+
Website:
|
|
29
|
+
Label: Website
|
|
30
|
+
Type: textinput
|
|
31
|
+
BillingStreet:
|
|
32
|
+
Label: Billing Street
|
|
33
|
+
Type: textinput
|
|
34
|
+
BillingCity:
|
|
35
|
+
Label: Billing City
|
|
36
|
+
Type: textinput
|
|
37
|
+
BillingState:
|
|
38
|
+
Label: Billing State
|
|
39
|
+
Type: textinput
|
|
40
|
+
BillingPostalCode:
|
|
41
|
+
Label: Billing Postal Code
|
|
42
|
+
Type: textinput
|
|
43
|
+
BillingCountry:
|
|
44
|
+
Label: Billing Country
|
|
45
|
+
Type: textinput
|
|
46
|
+
|
|
47
|
+
ObjectFields:
|
|
48
|
+
# these are reference fields to the other objects
|
|
49
|
+
|
|
50
|
+
TitleFields:
|
|
51
|
+
# these fields are concatenated to show the record
|
|
52
|
+
# in the Index form
|
|
53
|
+
- Name
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
Query_Fields:
|
|
2
|
+
# This is just a sample list of fields
|
|
3
|
+
# that are used in Query request
|
|
4
|
+
Id:
|
|
5
|
+
Label: Id
|
|
6
|
+
Type: id
|
|
7
|
+
Salutation:
|
|
8
|
+
Label: Salutation
|
|
9
|
+
Type: Picklist
|
|
10
|
+
FirstName:
|
|
11
|
+
Label: First Name
|
|
12
|
+
Type: textinput
|
|
13
|
+
LastName:
|
|
14
|
+
Label: Last Name
|
|
15
|
+
Type: textinput
|
|
16
|
+
Title:
|
|
17
|
+
Label: Title
|
|
18
|
+
Type: textinput
|
|
19
|
+
Email:
|
|
20
|
+
Label: Email
|
|
21
|
+
Type: textinput
|
|
22
|
+
Phone:
|
|
23
|
+
Label: Phone
|
|
24
|
+
Type: textinput
|
|
25
|
+
MailingStreet:
|
|
26
|
+
Label: Mailing Street
|
|
27
|
+
Type: textinput
|
|
28
|
+
MailingCity:
|
|
29
|
+
Label: Mailing City
|
|
30
|
+
Type: textinput
|
|
31
|
+
MailingState:
|
|
32
|
+
Label: Mailing State
|
|
33
|
+
Type: textinput
|
|
34
|
+
MailingPostalCode:
|
|
35
|
+
Label: Mailing Postal Code
|
|
36
|
+
Type: textinput
|
|
37
|
+
MailingCountry:
|
|
38
|
+
Label: Mailing Country
|
|
39
|
+
Type: textinput
|
|
40
|
+
LeadSource:
|
|
41
|
+
Label: Lead Source
|
|
42
|
+
Type: Picklist
|
|
43
|
+
Description:
|
|
44
|
+
Label: Description
|
|
45
|
+
Type: textarea
|
|
46
|
+
AccountId:
|
|
47
|
+
Lable: Account Id
|
|
48
|
+
Type: object
|
|
49
|
+
Object: Account
|
|
50
|
+
|
|
51
|
+
ObjectFields:
|
|
52
|
+
# these are reference fields to the other objects
|
|
53
|
+
AccountId:
|
|
54
|
+
|
|
55
|
+
TitleFields:
|
|
56
|
+
# these fields are concatenated to show the record
|
|
57
|
+
# in the Index form
|
|
58
|
+
- FirstName
|
|
59
|
+
- LastName
|
|
60
|
+
|
|
61
|
+
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
Query_Fields:
|
|
2
|
+
# This is just a sample list of fields
|
|
3
|
+
# that are used in Query request
|
|
4
|
+
Id:
|
|
5
|
+
Label: Id
|
|
6
|
+
Type: id
|
|
7
|
+
|
|
8
|
+
ObjectFields:
|
|
9
|
+
# these are reference fields to the other objects
|
|
10
|
+
|
|
11
|
+
TitleFields:
|
|
12
|
+
# these fields are concatenated to show the record
|
|
13
|
+
# in the Index form
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
Query_Fields:
|
|
2
|
+
# This is just a sample list of fields
|
|
3
|
+
# that are used in Query request
|
|
4
|
+
Id:
|
|
5
|
+
Label: Id
|
|
6
|
+
Type: id
|
|
7
|
+
Salutation:
|
|
8
|
+
Label: Salutation
|
|
9
|
+
Type: Picklist
|
|
10
|
+
FirstName:
|
|
11
|
+
Label: First Name
|
|
12
|
+
Type: textinput
|
|
13
|
+
LastName:
|
|
14
|
+
Label: Last Name
|
|
15
|
+
Type: textinput
|
|
16
|
+
Title:
|
|
17
|
+
Label: Title
|
|
18
|
+
Type: textinput
|
|
19
|
+
Company:
|
|
20
|
+
Label: Company
|
|
21
|
+
Type: textinput
|
|
22
|
+
Industry:
|
|
23
|
+
Label: Industry
|
|
24
|
+
Type: Picklist
|
|
25
|
+
AnnualRevenue:
|
|
26
|
+
Label: Annual Revenue
|
|
27
|
+
Type: textinput
|
|
28
|
+
NumberOfEmployees:
|
|
29
|
+
Label: Number Of Employees
|
|
30
|
+
Type: textinput
|
|
31
|
+
Street:
|
|
32
|
+
Label: Street
|
|
33
|
+
Type: textinput
|
|
34
|
+
City:
|
|
35
|
+
Label: City
|
|
36
|
+
Type: textinput
|
|
37
|
+
State:
|
|
38
|
+
Label: State
|
|
39
|
+
Type: textinput
|
|
40
|
+
PostalCode:
|
|
41
|
+
Label: Postal Code
|
|
42
|
+
Type: textinput
|
|
43
|
+
Country:
|
|
44
|
+
Label: Country
|
|
45
|
+
Type: textinput
|
|
46
|
+
Email:
|
|
47
|
+
Label: Email
|
|
48
|
+
Type: textinput
|
|
49
|
+
Phone:
|
|
50
|
+
Label: Phone
|
|
51
|
+
Type: textinput
|
|
52
|
+
Description:
|
|
53
|
+
Label: Description
|
|
54
|
+
Type: textarea
|
|
55
|
+
LeadSource:
|
|
56
|
+
Label: Lead Source
|
|
57
|
+
Type: Picklist
|
|
58
|
+
Status:
|
|
59
|
+
Label: Status
|
|
60
|
+
Type: Picklist
|
|
61
|
+
Rating:
|
|
62
|
+
Label: Rating
|
|
63
|
+
Type: Picklist
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
ObjectFields:
|
|
67
|
+
# these are reference fields to the other objects
|
|
68
|
+
|
|
69
|
+
TitleFields:
|
|
70
|
+
# these fields are concatenated to show the record
|
|
71
|
+
# in the Index form
|
|
72
|
+
- FirstName
|
|
73
|
+
- LastName
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
Query_Fields:
|
|
2
|
+
# This is just a sample list of fields
|
|
3
|
+
# that are used in Query request
|
|
4
|
+
Id:
|
|
5
|
+
Label: Id
|
|
6
|
+
Type: id
|
|
7
|
+
Name:
|
|
8
|
+
Label: Opportunity Name
|
|
9
|
+
Type: textinput
|
|
10
|
+
AccountId:
|
|
11
|
+
Label: Account Id
|
|
12
|
+
Type: object
|
|
13
|
+
Object: Account
|
|
14
|
+
StageName:
|
|
15
|
+
Label: Stage
|
|
16
|
+
Type: Picklist
|
|
17
|
+
Amount:
|
|
18
|
+
Label: Amount
|
|
19
|
+
Type: textarea
|
|
20
|
+
Probability:
|
|
21
|
+
Label: Probability
|
|
22
|
+
Type: textarea
|
|
23
|
+
CloseDate:
|
|
24
|
+
Label: Close Date
|
|
25
|
+
Type: datetime
|
|
26
|
+
Type:
|
|
27
|
+
Label: Opportunity Type
|
|
28
|
+
Type: Picklist
|
|
29
|
+
NextStep:
|
|
30
|
+
Label: Next Step
|
|
31
|
+
Type: textinput
|
|
32
|
+
LeadSource:
|
|
33
|
+
Label: Lead Source
|
|
34
|
+
Type: Picklist
|
|
35
|
+
Description:
|
|
36
|
+
Label: Description
|
|
37
|
+
Type: textarea
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
ObjectFields:
|
|
41
|
+
# these are reference fields to the other objects
|
|
42
|
+
AccountId:
|
|
43
|
+
|
|
44
|
+
TitleFields:
|
|
45
|
+
# these fields are concatenated to show the record
|
|
46
|
+
# in the Index form
|
|
47
|
+
- Name
|
|
48
|
+
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
require File.join(File.dirname(__FILE__),'spec_helper')
|
|
2
|
+
|
|
3
|
+
describe "Application" do
|
|
4
|
+
it_should_behave_like "SpecHelper" do
|
|
5
|
+
it "should authenticate" do
|
|
6
|
+
Application.authenticate(@test_user,@test_password,nil).should be_true
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
it "should not authenticate with wrong credentials" do
|
|
10
|
+
Application.should_receive(:warn).once.with(/Can't authenticate user wrong_user: 500 Internal Server Error/)
|
|
11
|
+
Application.authenticate('wrong_user','wrong_password',nil).should be_false
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
end
|