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,301 @@
|
|
|
1
|
+
require 'rhoconnect-adapters'
|
|
2
|
+
require 'vendor/ms_dynamics/ms_dynamics'
|
|
3
|
+
require 'active_support/inflector'
|
|
4
|
+
|
|
5
|
+
module RhoconnectAdapters
|
|
6
|
+
module CRM
|
|
7
|
+
module MsDynamics
|
|
8
|
+
class Adapter < SourceAdapter
|
|
9
|
+
attr_accessor :crm_object
|
|
10
|
+
attr_accessor :fields
|
|
11
|
+
|
|
12
|
+
def initialize(source)
|
|
13
|
+
super(source)
|
|
14
|
+
@crm_object = self.class.name
|
|
15
|
+
@fields = {}
|
|
16
|
+
@title_fields = ["#{crm_object.downcase}id"]
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def configure_fields
|
|
20
|
+
# initialize fields map
|
|
21
|
+
@fields = get_object_settings['Query_Fields']
|
|
22
|
+
@field_picklists = {}
|
|
23
|
+
|
|
24
|
+
# obtain attribute type picklists
|
|
25
|
+
@attrtype_picklists = {}
|
|
26
|
+
attribute_type_picklists = get_object_settings['AttributeTypePicklists']
|
|
27
|
+
if attribute_type_picklists != nil
|
|
28
|
+
attribute_type_picklists.each do |element_name, values|
|
|
29
|
+
@attrtype_picklists[element_name] = values
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
@object_fields = get_object_settings['ObjectFields']
|
|
34
|
+
@object_fields = {} if @object_fields == nil
|
|
35
|
+
|
|
36
|
+
# title fields are used in metadata to show
|
|
37
|
+
# records in the list
|
|
38
|
+
@title_fields = get_object_settings['TitleFields']
|
|
39
|
+
|
|
40
|
+
@fields
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def get_object_settings
|
|
44
|
+
return @object_settings if @object_settings
|
|
45
|
+
begin
|
|
46
|
+
@object_settings = RhoconnectAdapters::CRM::Field.load_file(File.join(ROOT_PATH,'vendor','ms_dynamics','settings',"#{crm_object.downcase}.yml"))
|
|
47
|
+
rescue Exception => e
|
|
48
|
+
puts "Error opening CRMObjects settings file: #{e}"
|
|
49
|
+
puts e.backtrace.join("\n")
|
|
50
|
+
raise e
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def get_picklists
|
|
55
|
+
begin
|
|
56
|
+
fields.each do |element_name, element_def|
|
|
57
|
+
# exclude artificial attribute type fields
|
|
58
|
+
next if @attrtype_picklists.has_key?(element_name)
|
|
59
|
+
data_type = element_def['Type']
|
|
60
|
+
# for picklists - get values
|
|
61
|
+
# but only for those that are not
|
|
62
|
+
# already defined statically
|
|
63
|
+
if data_type == 'Picklist' and not @field_picklists.has_key?(element_name)
|
|
64
|
+
@field_picklists[element_name] = get_picklist(element_name)
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
rescue RestClient::Exception => e
|
|
68
|
+
raise e
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
def get_picklist(element_name)
|
|
73
|
+
# check if we already have it in Store
|
|
74
|
+
picklist = Store.get_data("#{crm_object}:#{element_name}_picklist",Hash)
|
|
75
|
+
return picklist['picklist_vals'] if picklist.size != 0
|
|
76
|
+
|
|
77
|
+
field_values = @crm_metadata_service.request_picklist("#{crm_object.downcase}",element_name)
|
|
78
|
+
Store.put_data("#{crm_object}:#{element_name}_picklist", { 'picklist_vals' => field_values })
|
|
79
|
+
field_values
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
def login
|
|
83
|
+
auth_info = RhoconnectAdapters::CRM::MsDynamics.load_auth_info("#{current_user.login}")
|
|
84
|
+
@endpoint_url = auth_info['crm_service_url']
|
|
85
|
+
@crm_service = RhoconnectAdapters::CRM::MsDynamics::CrmService.new(@endpoint_url, auth_info['crm_ticket'], auth_info['user_organization'])
|
|
86
|
+
@crm_metadata_service = RhoconnectAdapters::CRM::MsDynamics::CrmMetadataService.new(auth_info['crm_metadata_service_url'], auth_info['crm_ticket'], auth_info['user_organization'])
|
|
87
|
+
# query picklist values
|
|
88
|
+
get_picklists
|
|
89
|
+
end
|
|
90
|
+
|
|
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
|
+
|
|
100
|
+
attributes = []
|
|
101
|
+
# strip out artificial 'attrtype' fields
|
|
102
|
+
fields.each do |key, val|
|
|
103
|
+
attributes << key unless key.index('_attrtype') != nil
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
@result = @crm_service.retrieve_multiple(crm_object.downcase,attributes,@field_picklists)
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
def metadata
|
|
110
|
+
# define the metadata
|
|
111
|
+
show_fields = []
|
|
112
|
+
new_fields = []
|
|
113
|
+
edit_fields = []
|
|
114
|
+
model_name = "" + crm_object
|
|
115
|
+
model_name[0] = model_name[0,1].downcase
|
|
116
|
+
record_sym = '@' + "#{model_name}"
|
|
117
|
+
fields.each do |element_name,element_def|
|
|
118
|
+
next if element_name == "#{crm_object.downcase}id"
|
|
119
|
+
|
|
120
|
+
# 1) - read-only show fields
|
|
121
|
+
field_type = 'labeledvalueli'
|
|
122
|
+
field = {
|
|
123
|
+
:name => "#{model_name}\[#{element_name}\]",
|
|
124
|
+
:label => element_def['Label'],
|
|
125
|
+
:type => field_type,
|
|
126
|
+
:value => "{{#{record_sym}/#{element_name}}}"
|
|
127
|
+
}
|
|
128
|
+
show_fields << field
|
|
129
|
+
|
|
130
|
+
new_field = field.clone
|
|
131
|
+
new_field[:type] = 'labeledinputli'
|
|
132
|
+
new_field.delete(:value)
|
|
133
|
+
case element_def['Type']
|
|
134
|
+
when 'Picklist'
|
|
135
|
+
new_field[:type] = 'select'
|
|
136
|
+
values = []
|
|
137
|
+
# attribute type fields should be treated specially
|
|
138
|
+
if element_name.index('_attrtype') != nil
|
|
139
|
+
values.concat @attrtype_picklists[element_name]
|
|
140
|
+
else
|
|
141
|
+
values[0] = nil
|
|
142
|
+
values.concat @field_picklists[element_name].values
|
|
143
|
+
end
|
|
144
|
+
new_field[:values] = values
|
|
145
|
+
new_field[:value] = values[0]
|
|
146
|
+
when 'object'
|
|
147
|
+
end
|
|
148
|
+
|
|
149
|
+
new_fields << new_field if not element_def['Type'] == 'object'
|
|
150
|
+
|
|
151
|
+
edit_field = new_field.clone
|
|
152
|
+
edit_field[:value] = "{{#{record_sym}/#{element_name}}}"
|
|
153
|
+
edit_fields << edit_field
|
|
154
|
+
end
|
|
155
|
+
|
|
156
|
+
# Show
|
|
157
|
+
show_list = { :name => 'list', :type => 'list', :children => show_fields }
|
|
158
|
+
show_form = {
|
|
159
|
+
:name => "#{crm_object}_show",
|
|
160
|
+
:type => 'show_form',
|
|
161
|
+
:title => "#{crm_object} details",
|
|
162
|
+
:object => "#{crm_object}",
|
|
163
|
+
:model => "#{model_name}",
|
|
164
|
+
:id => "{{#{record_sym}/object}}",
|
|
165
|
+
:children => [show_list]
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
# New
|
|
169
|
+
new_list = show_list.clone
|
|
170
|
+
new_list[:children] = new_fields
|
|
171
|
+
new_form = {
|
|
172
|
+
:type => 'new_form',
|
|
173
|
+
:title => "New #{crm_object}",
|
|
174
|
+
:object => "#{crm_object}",
|
|
175
|
+
:model => "#{model_name}",
|
|
176
|
+
:children => [new_list]
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
# Edit
|
|
180
|
+
edit_list = show_list.clone
|
|
181
|
+
edit_list[:children] = edit_fields
|
|
182
|
+
edit_form = {
|
|
183
|
+
:type => 'update_form',
|
|
184
|
+
:title => "Edit #{crm_object}",
|
|
185
|
+
:object => "#{crm_object}",
|
|
186
|
+
:model => "#{model_name}",
|
|
187
|
+
:id => "{{#{record_sym}/object}}",
|
|
188
|
+
:children => [edit_list]
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
# Index
|
|
192
|
+
title_field_metadata = @title_fields.collect { |field_name | "{{#{field_name.to_s}}} " }.join(' ')
|
|
193
|
+
object_rec = {
|
|
194
|
+
:object => "#{crm_object}",
|
|
195
|
+
:id => "{{object}}",
|
|
196
|
+
:type => 'linkobj',
|
|
197
|
+
:text => "#{title_field_metadata}"
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
index_form = {
|
|
201
|
+
:title => "#{crm_object.pluralize}",
|
|
202
|
+
:object => "#{crm_object}",
|
|
203
|
+
:type => 'index_form',
|
|
204
|
+
:children => [object_rec],
|
|
205
|
+
:repeatable => "{{#{record_sym.pluralize}}}"
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
# return JSON
|
|
209
|
+
{ 'index' => index_form, 'show' => show_form, 'new' => new_form, 'edit' => edit_form }.to_json
|
|
210
|
+
end
|
|
211
|
+
|
|
212
|
+
def create(create_hash,blob=nil)
|
|
213
|
+
# TODO: Create a new record in your backend data source
|
|
214
|
+
# If your rhodes rhom object contains image/binary data
|
|
215
|
+
# (has the image_uri attribute), then a blob will be provided
|
|
216
|
+
created_object_id = nil
|
|
217
|
+
request_fields = {}
|
|
218
|
+
field_types = {}
|
|
219
|
+
fields.each do |element_name, element_def|
|
|
220
|
+
field_value = create_hash[element_name]
|
|
221
|
+
|
|
222
|
+
# special case scenario where the field is
|
|
223
|
+
# actually a 'type' attribute of another field
|
|
224
|
+
type_index = element_name.index('_attrtype')
|
|
225
|
+
if type_index != nil
|
|
226
|
+
field_name = element_name.slice(0, type_index)
|
|
227
|
+
field_types[field_name] = field_value
|
|
228
|
+
next
|
|
229
|
+
end
|
|
230
|
+
|
|
231
|
+
# convert Picklist field values from User-friendly form
|
|
232
|
+
# into Integers that are accepted by MsDynamics
|
|
233
|
+
if @field_picklists.has_key?(element_name)
|
|
234
|
+
field_picklist_indexes = @field_picklists[element_name].invert
|
|
235
|
+
field_value = field_picklist_indexes[field_value]
|
|
236
|
+
end
|
|
237
|
+
|
|
238
|
+
if field_value != nil and element_name != "#{crm_object.downcase}id"
|
|
239
|
+
request_fields[element_name] = field_value
|
|
240
|
+
end
|
|
241
|
+
end
|
|
242
|
+
|
|
243
|
+
created_object_id = @crm_service.create(crm_object.downcase, request_fields, field_types)
|
|
244
|
+
end
|
|
245
|
+
def update(update_hash)
|
|
246
|
+
# it may be there as 'id' field
|
|
247
|
+
updated_object_id = update_hash["#{crm_object.downcase}id"] || update_hash['id']
|
|
248
|
+
if updated_object_id == nil
|
|
249
|
+
raise SourceAdapterObjectConflictError.new("Either '#{crm_object.downcase}id' or 'id' field must be specified for the Update request")
|
|
250
|
+
end
|
|
251
|
+
|
|
252
|
+
request_fields = {}
|
|
253
|
+
field_types = {}
|
|
254
|
+
fields.each do |element_name,element_def|
|
|
255
|
+
next unless element_name != "#{crm_object.downcase}id"
|
|
256
|
+
|
|
257
|
+
field_value = update_hash[element_name]
|
|
258
|
+
|
|
259
|
+
# special case scenario where the field is
|
|
260
|
+
# actually a 'type' attribute of another field
|
|
261
|
+
type_index = element_name.index('_attrtype')
|
|
262
|
+
if type_index != nil
|
|
263
|
+
field_name = element_name.slice(0, type_index)
|
|
264
|
+
field_types[field_name] = field_value
|
|
265
|
+
next
|
|
266
|
+
end
|
|
267
|
+
|
|
268
|
+
# convert Picklist field values from User-friendly form
|
|
269
|
+
# into Integers that are accepted by MsDynamics
|
|
270
|
+
if @field_picklists.has_key?(element_name)
|
|
271
|
+
field_picklist_indexes = @field_picklists[element_name].invert
|
|
272
|
+
field_value = field_picklist_indexes[field_value]
|
|
273
|
+
end
|
|
274
|
+
|
|
275
|
+
if field_value != nil
|
|
276
|
+
request_fields[element_name] = field_value
|
|
277
|
+
end
|
|
278
|
+
end
|
|
279
|
+
|
|
280
|
+
@crm_service.update(crm_object.downcase, updated_object_id, request_fields, field_types)
|
|
281
|
+
updated_object_id
|
|
282
|
+
end
|
|
283
|
+
|
|
284
|
+
def delete(delete_hash)
|
|
285
|
+
deleted_object_id = delete_hash["#{crm_object.downcase}id"] || delete_hash['id']
|
|
286
|
+
|
|
287
|
+
if deleted_object_id == nil
|
|
288
|
+
raise SourceAdapterObjectConflictError.new("Either '#{crm_object.downcase}id' or 'id' field must be specified for the Delete request")
|
|
289
|
+
end
|
|
290
|
+
|
|
291
|
+
@crm_service.delete(crm_object.downcase, deleted_object_id)
|
|
292
|
+
deleted_object_id
|
|
293
|
+
end
|
|
294
|
+
|
|
295
|
+
def logoff
|
|
296
|
+
# logoff if necessary
|
|
297
|
+
end
|
|
298
|
+
end
|
|
299
|
+
end
|
|
300
|
+
end
|
|
301
|
+
end
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
require 'rhoconnect-adapters'
|
|
2
|
+
require 'vendor/ms_dynamics/ms_dynamics'
|
|
3
|
+
|
|
4
|
+
module RhoconnectAdapters
|
|
5
|
+
module CRM
|
|
6
|
+
module MsDynamics
|
|
7
|
+
class Application < Rhoconnect::Base
|
|
8
|
+
class << self
|
|
9
|
+
def authenticate(username,password,session)
|
|
10
|
+
begin
|
|
11
|
+
# TODO: handle exceptions
|
|
12
|
+
# From time to time Win Live doesn't respond or returns 400 (Bad Request);
|
|
13
|
+
# we probably should retry in such cases
|
|
14
|
+
wlid_ticket, wlid_expires =
|
|
15
|
+
RhoconnectAdapters::CRM::MsDynamics::WlidService.get_ticket(username,password)
|
|
16
|
+
crm_service_url, crm_metadata_service_url, crm_ticket, crm_ticket_expires, user_organization =
|
|
17
|
+
RhoconnectAdapters::CRM::MsDynamics::DiscoveryService.get_crm_ticket(get_settings[:msdynamics_ticket_url],wlid_ticket)
|
|
18
|
+
# store recieved tickets and associated information in redis for the future reference
|
|
19
|
+
RhoconnectAdapters::CRM::MsDynamics.save_auth_info(username,
|
|
20
|
+
{
|
|
21
|
+
"wlid_ticket" => wlid_ticket,
|
|
22
|
+
"wlid_expires" => wlid_expires.to_s,
|
|
23
|
+
"crm_service_url" => crm_service_url,
|
|
24
|
+
"crm_metadata_service_url" => crm_metadata_service_url,
|
|
25
|
+
"crm_ticket" => crm_ticket,
|
|
26
|
+
"crm_ticket_expires" => crm_ticket_expires.to_s,
|
|
27
|
+
"user_organization" => user_organization
|
|
28
|
+
})
|
|
29
|
+
rescue Exception => ex
|
|
30
|
+
warn "Can't authenticate user #{username}: " + ex.inspect
|
|
31
|
+
return false
|
|
32
|
+
end
|
|
33
|
+
true
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def get_settings
|
|
37
|
+
return @settings if @settings
|
|
38
|
+
begin
|
|
39
|
+
file = YAML.load_file(File.join(ROOT_PATH,'settings','settings.yml'))
|
|
40
|
+
env = (ENV['RHO_ENV'] || :development).to_sym
|
|
41
|
+
@settings = file[env]
|
|
42
|
+
|
|
43
|
+
# vendor-specific settings
|
|
44
|
+
file = YAML.load_file(File.join(ROOT_PATH,'vendor','ms_dynamics','settings','settings.yml'))
|
|
45
|
+
@settings.merge!(file[env])
|
|
46
|
+
rescue Exception => e
|
|
47
|
+
puts "Error opening settings file: #{e}"
|
|
48
|
+
puts e.backtrace.join("\n")
|
|
49
|
+
raise e
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
end
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
module RhoconnectAdapters
|
|
2
|
+
module CRM
|
|
3
|
+
module MsDynamics
|
|
4
|
+
class CrmMetadataService < SoapService
|
|
5
|
+
def initialize(crm_metadata_service_url,crm_ticket,user_organization)
|
|
6
|
+
@crm_metadata_service_url = crm_metadata_service_url
|
|
7
|
+
@message_header = "
|
|
8
|
+
<CrmAuthenticationToken xmlns=\"http://schemas.microsoft.com/crm/2007/WebServices\">
|
|
9
|
+
<AuthenticationType xmlns=\"http://schemas.microsoft.com/crm/2007/CoreTypes\">1</AuthenticationType>
|
|
10
|
+
<CrmTicket xmlns=\"http://schemas.microsoft.com/crm/2007/CoreTypes\">#{crm_ticket}</CrmTicket>
|
|
11
|
+
<OrganizationName xmlns=\"http://schemas.microsoft.com/crm/2007/CoreTypes\">#{user_organization}</OrganizationName>
|
|
12
|
+
<CallerId xmlns=\"http://schemas.microsoft.com/crm/2007/CoreTypes\">00000000-0000-0000-0000-000000000000</CallerId>
|
|
13
|
+
</CrmAuthenticationToken>"
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def request_picklist(entity_name,attribute)
|
|
17
|
+
message = SoapService.compose_message(@message_header,
|
|
18
|
+
"<Execute xmlns=\"http://schemas.microsoft.com/crm/2007/WebServices\">
|
|
19
|
+
<Request xsi:type=\"RetrieveAttributeRequest\">
|
|
20
|
+
<EntityLogicalName>#{entity_name}</EntityLogicalName>
|
|
21
|
+
<LogicalName>#{attribute}</LogicalName>
|
|
22
|
+
<RetrieveAsIfPublished>1</RetrieveAsIfPublished>
|
|
23
|
+
</Request>
|
|
24
|
+
</Execute>")
|
|
25
|
+
doc = SoapService.send_request(@crm_metadata_service_url,message,get_action('Execute'))
|
|
26
|
+
result = {}
|
|
27
|
+
options = SoapService.select_node(doc, '//cws7:Option')
|
|
28
|
+
options.each do |option|
|
|
29
|
+
value = SoapService.select_node_text(option, 'cws7:Value')
|
|
30
|
+
result[value] = SoapService.select_node_text(option, 'cws7:Label/cws7:LocLabels/cws7:LocLabel/cws7:Label')
|
|
31
|
+
end
|
|
32
|
+
result
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
private
|
|
36
|
+
def get_action(name)
|
|
37
|
+
"http://schemas.microsoft.com/crm/2007/WebServices/#{name}"
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
module RhoconnectAdapters
|
|
2
|
+
module CRM
|
|
3
|
+
module MsDynamics
|
|
4
|
+
class CrmService < SoapService
|
|
5
|
+
def initialize(crm_service_url,crm_ticket,user_organization)
|
|
6
|
+
@crm_service_url = crm_service_url
|
|
7
|
+
@message_header = "
|
|
8
|
+
<CrmAuthenticationToken xmlns=\"http://schemas.microsoft.com/crm/2007/WebServices\">
|
|
9
|
+
<AuthenticationType xmlns=\"http://schemas.microsoft.com/crm/2007/CoreTypes\">1</AuthenticationType>
|
|
10
|
+
<CrmTicket xmlns=\"http://schemas.microsoft.com/crm/2007/CoreTypes\">#{crm_ticket}</CrmTicket>
|
|
11
|
+
<OrganizationName xmlns=\"http://schemas.microsoft.com/crm/2007/CoreTypes\">#{user_organization}</OrganizationName>
|
|
12
|
+
<CallerId xmlns=\"http://schemas.microsoft.com/crm/2007/CoreTypes\">00000000-0000-0000-0000-000000000000</CallerId>
|
|
13
|
+
</CrmAuthenticationToken>"
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def request(request_name)
|
|
17
|
+
message = SoapService.compose_message(@message_header,
|
|
18
|
+
"<Execute xmlns=\"http://schemas.microsoft.com/crm/2007/WebServices\">
|
|
19
|
+
<Request xsi:type=\"#{request_name}\"/>
|
|
20
|
+
</Execute>")
|
|
21
|
+
SoapService.send_request(@crm_service_url,message,get_action('Execute'))
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def retrieve(entity_name,entity_id,attributes)
|
|
25
|
+
message = SoapService.compose_message(@message_header,
|
|
26
|
+
"<Retrieve xmlns='http://schemas.microsoft.com/crm/2007/WebServices'>
|
|
27
|
+
<entityName>#{entity_name}</entityName>
|
|
28
|
+
<id>#{entity_id}</id>
|
|
29
|
+
<columnSet xmlns:q1='http://schemas.microsoft.com/crm/2006/Query' xsi:type='q1:ColumnSet'>
|
|
30
|
+
<q1:Attributes>
|
|
31
|
+
#{get_columns(attributes)}
|
|
32
|
+
</q1:Attributes>
|
|
33
|
+
</columnSet>
|
|
34
|
+
</Retrieve>")
|
|
35
|
+
doc = SoapService.send_request(@crm_service_url,message,get_action('Retrieve'))
|
|
36
|
+
res = {}
|
|
37
|
+
attributes.each do |attribute|
|
|
38
|
+
res.merge!(attribute => SoapService.select_node_text(doc,"//cws7:#{attribute}"))
|
|
39
|
+
end
|
|
40
|
+
res
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def retrieve_multiple(entity_name, attributes, field_picklists_map, distinct=true, criteria_xml="")
|
|
44
|
+
message = SoapService.compose_message(@message_header,
|
|
45
|
+
"<RetrieveMultiple xmlns='http://schemas.microsoft.com/crm/2007/WebServices'>
|
|
46
|
+
<query xmlns:q1='http://schemas.microsoft.com/crm/2006/Query' xsi:type='q1:QueryExpression'>
|
|
47
|
+
<q1:EntityName>#{entity_name}</q1:EntityName>
|
|
48
|
+
<q1:ColumnSet xsi:type='q1:ColumnSet'>
|
|
49
|
+
<q1:Attributes>
|
|
50
|
+
#{get_columns(attributes)}
|
|
51
|
+
</q1:Attributes>
|
|
52
|
+
</q1:ColumnSet>
|
|
53
|
+
<q1:Distinct>#{distinct.to_s}</q1:Distinct>
|
|
54
|
+
#{criteria_xml}
|
|
55
|
+
</query>
|
|
56
|
+
</RetrieveMultiple>")
|
|
57
|
+
doc = SoapService.send_request(@crm_service_url,message,get_action('RetrieveMultiple'))
|
|
58
|
+
business_entities = SoapService.select_node(doc,'//cws6:BusinessEntity')
|
|
59
|
+
result = {}
|
|
60
|
+
business_entities.each do |business_entity|
|
|
61
|
+
record = {}
|
|
62
|
+
business_entity.children.each do |field|
|
|
63
|
+
type_field = field.attributes['type']
|
|
64
|
+
record.merge!("#{field.name}_attrtype" => type_field) unless type_field.nil?
|
|
65
|
+
field_value = field.text
|
|
66
|
+
# convert IntegerValues for Picklist types into User-friendly strings
|
|
67
|
+
field_value = field_picklists_map[field.name][field.text] unless field_picklists_map[field.name].nil?
|
|
68
|
+
record.merge!(field.name => field_value)
|
|
69
|
+
end
|
|
70
|
+
clean_attributes(entity_name,record)
|
|
71
|
+
field_id = record["#{entity_name}id"]
|
|
72
|
+
result[field_id] = record unless field_id.nil?
|
|
73
|
+
end
|
|
74
|
+
result
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
def create(entity_name,params,types={})
|
|
78
|
+
message = SoapService.compose_message(@message_header,
|
|
79
|
+
"<Create xmlns='http://schemas.microsoft.com/crm/2007/WebServices'>
|
|
80
|
+
<entity xsi:type='#{entity_name}'>
|
|
81
|
+
#{get_params(params,types)}
|
|
82
|
+
</entity>
|
|
83
|
+
</Create>")
|
|
84
|
+
doc = SoapService.send_request(@crm_service_url,message,get_action('Create'))
|
|
85
|
+
SoapService.select_node_text(doc,'//cws7:CreateResult')
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
def update(entity_name,entity_id,params,types={})
|
|
89
|
+
message = SoapService.compose_message(@message_header,
|
|
90
|
+
"<Update xmlns='http://schemas.microsoft.com/crm/2007/WebServices'>
|
|
91
|
+
<entity xsi:type='#{entity_name}'>
|
|
92
|
+
#{get_params(params,types)}
|
|
93
|
+
<#{entity_name}id>#{entity_id}</#{entity_name}id>
|
|
94
|
+
</entity>
|
|
95
|
+
</Update>")
|
|
96
|
+
SoapService.send_request(@crm_service_url,message,get_action('Update'))
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
def delete(entity_name,entity_id)
|
|
100
|
+
message = SoapService.compose_message(@message_header,
|
|
101
|
+
"<Delete xmlns='http://schemas.microsoft.com/crm/2007/WebServices'>
|
|
102
|
+
<entityName>#{entity_name}</entityName>
|
|
103
|
+
<id>#{entity_id}</id>
|
|
104
|
+
</Delete>")
|
|
105
|
+
SoapService.send_request(@crm_service_url,message,get_action('Delete'))
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
def get_current_user
|
|
109
|
+
doc = request('WhoAmIRequest')
|
|
110
|
+
SoapService.select_node_text(doc,'//cws7:UserId')
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
private
|
|
114
|
+
def get_action(name)
|
|
115
|
+
"http://schemas.microsoft.com/crm/2007/WebServices/#{name}"
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
def get_columns(attributes)
|
|
119
|
+
columns = attributes.collect { |attrib| "<q1:Attribute>#{attrib}</q1:Attribute>" }
|
|
120
|
+
columns.to_s
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
def get_params(params, types = {})
|
|
124
|
+
res = []
|
|
125
|
+
params.each do |name, value|
|
|
126
|
+
type_attr = " type='#{types[name].to_s}'" unless types[name].nil?
|
|
127
|
+
res << "<#{name}#{type_attr}>#{value}</#{name}>"
|
|
128
|
+
end
|
|
129
|
+
res.to_s
|
|
130
|
+
end
|
|
131
|
+
|
|
132
|
+
def clean_attributes(entity_name,attributes)
|
|
133
|
+
name = "#{entity_name}id"
|
|
134
|
+
id = attributes[name]
|
|
135
|
+
attributes[name] = id[1..-2] if id
|
|
136
|
+
attributes
|
|
137
|
+
end
|
|
138
|
+
end
|
|
139
|
+
end
|
|
140
|
+
end
|
|
141
|
+
end
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
module RhoconnectAdapters
|
|
2
|
+
module CRM
|
|
3
|
+
module MsDynamics
|
|
4
|
+
class DiscoveryService < SoapService
|
|
5
|
+
|
|
6
|
+
# Get the Crm Service Url, Crm Ticket, Crm Ticket expiration date/time, and Unique User Organization Name
|
|
7
|
+
def self.get_crm_ticket(host_name,wlid_ticket)
|
|
8
|
+
@wlid_ticket = wlid_ticket
|
|
9
|
+
@discovery_service_url = "https://#{host_name}/MSCRMServices/2007/Passport/CrmDiscoveryService.asmx"
|
|
10
|
+
retrieve_ticket(get_user_organization)
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
private
|
|
14
|
+
class << self
|
|
15
|
+
def execute_discovery_request(request,params='')
|
|
16
|
+
body = "
|
|
17
|
+
<Execute xmlns=\"http://schemas.microsoft.com/crm/2007/CrmDiscoveryService\">
|
|
18
|
+
<Request xsi:type=\"#{request}\">
|
|
19
|
+
#{params}
|
|
20
|
+
<PassportTicket>#{@wlid_ticket}</PassportTicket>
|
|
21
|
+
</Request>
|
|
22
|
+
</Execute>"
|
|
23
|
+
send_request(@discovery_service_url,compose_message(nil,body),
|
|
24
|
+
"http://schemas.microsoft.com/crm/2007/CrmDiscoveryService/Execute")
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
# Retrieve a list of organizations that the logged on user is a member of
|
|
28
|
+
# and select unique name of the first one
|
|
29
|
+
# TODO: do we need to handle case when user is member of more then one org?
|
|
30
|
+
def get_user_organization
|
|
31
|
+
doc = execute_discovery_request('RetrieveOrganizationsRequest')
|
|
32
|
+
select_node_text(doc,'//cds:OrganizationName[1]')
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
# Retrieve the Crm Service Url, Crm Ticket, Crm Ticket expiration date/time, and Unique User Organization Name
|
|
36
|
+
def retrieve_ticket(user_organization)
|
|
37
|
+
doc = execute_discovery_request('RetrieveCrmTicketRequest',"<OrganizationName>#{user_organization}</OrganizationName>")
|
|
38
|
+
crm_ticket = select_node_text(doc,'//cds:CrmTicket')
|
|
39
|
+
crm_ticket_expires = DateTime.parse(select_node_text(doc,'//cds:ExpirationDate'))
|
|
40
|
+
crm_service_url = select_node_text(doc,'//cds:CrmServiceUrl')
|
|
41
|
+
crm_metadata_service_url = select_node_text(doc, '//cds:CrmMetadataServiceUrl')
|
|
42
|
+
[crm_service_url, crm_metadata_service_url, crm_ticket, crm_ticket_expires, user_organization]
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
|