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.
Files changed (106) hide show
  1. data/.gitignore +4 -0
  2. data/CHANGELOG +4 -0
  3. data/Gemfile +18 -0
  4. data/README.md +239 -0
  5. data/Rakefile +31 -0
  6. data/bin/rhoconnect-adapters +35 -0
  7. data/generators/crm/templates/application/application.rb +34 -0
  8. data/generators/crm/templates/source/source_adapter.rb +10 -0
  9. data/generators/crm/templates/source/source_spec.rb +25 -0
  10. data/generators/crm/templates/spec/spec_helper.rb +64 -0
  11. data/generators/crm/vendor/ms_dynamics/adapter.rb +301 -0
  12. data/generators/crm/vendor/ms_dynamics/application.rb +56 -0
  13. data/generators/crm/vendor/ms_dynamics/lib/crm_metadata_service.rb +43 -0
  14. data/generators/crm/vendor/ms_dynamics/lib/crm_service.rb +141 -0
  15. data/generators/crm/vendor/ms_dynamics/lib/discovery_service.rb +51 -0
  16. data/generators/crm/vendor/ms_dynamics/lib/wlid_service.rb +159 -0
  17. data/generators/crm/vendor/ms_dynamics/ms_dynamics.rb +45 -0
  18. data/generators/crm/vendor/ms_dynamics/settings/Account.yml +46 -0
  19. data/generators/crm/vendor/ms_dynamics/settings/Contact.yml +40 -0
  20. data/generators/crm/vendor/ms_dynamics/settings/GenericObject.yml +18 -0
  21. data/generators/crm/vendor/ms_dynamics/settings/Lead.yml +53 -0
  22. data/generators/crm/vendor/ms_dynamics/settings/Opportunity.yml +41 -0
  23. data/generators/crm/vendor/ms_dynamics/settings/settings.yml +9 -0
  24. data/generators/crm/vendor/ms_dynamics/spec/application_spec.rb +23 -0
  25. data/generators/crm/vendor/ms_dynamics/spec/sources/account_spec.rb +49 -0
  26. data/generators/crm/vendor/ms_dynamics/spec/sources/contact_spec.rb +49 -0
  27. data/generators/crm/vendor/ms_dynamics/spec/sources/lead_spec.rb +49 -0
  28. data/generators/crm/vendor/ms_dynamics/spec/sources/opportunity_spec.rb +62 -0
  29. data/generators/crm/vendor/ms_dynamics/spec/spec_helper.rb +78 -0
  30. data/generators/crm/vendor/ms_dynamics/spec_data/Account.yml +6 -0
  31. data/generators/crm/vendor/ms_dynamics/spec_data/Contact.yml +7 -0
  32. data/generators/crm/vendor/ms_dynamics/spec_data/GenericObject.yml +3 -0
  33. data/generators/crm/vendor/ms_dynamics/spec_data/Lead.yml +12 -0
  34. data/generators/crm/vendor/ms_dynamics/spec_data/Opportunity.yml +6 -0
  35. data/generators/crm/vendor/ms_dynamics/templates.rb +57 -0
  36. data/generators/crm/vendor/oracle_on_demand/adapter.rb +421 -0
  37. data/generators/crm/vendor/oracle_on_demand/application.rb +64 -0
  38. data/generators/crm/vendor/oracle_on_demand/settings/Account.yml +91 -0
  39. data/generators/crm/vendor/oracle_on_demand/settings/Contact.yml +54 -0
  40. data/generators/crm/vendor/oracle_on_demand/settings/GenericObject.yml +21 -0
  41. data/generators/crm/vendor/oracle_on_demand/settings/Lead.yml +72 -0
  42. data/generators/crm/vendor/oracle_on_demand/settings/Opportunity.yml +69 -0
  43. data/generators/crm/vendor/oracle_on_demand/settings/settings.yml +8 -0
  44. data/generators/crm/vendor/oracle_on_demand/spec/application_spec.rb +14 -0
  45. data/generators/crm/vendor/oracle_on_demand/spec/sources/account_spec.rb +50 -0
  46. data/generators/crm/vendor/oracle_on_demand/spec/sources/contact_spec.rb +50 -0
  47. data/generators/crm/vendor/oracle_on_demand/spec/sources/lead_spec.rb +51 -0
  48. data/generators/crm/vendor/oracle_on_demand/spec/sources/opportunity_spec.rb +51 -0
  49. data/generators/crm/vendor/oracle_on_demand/spec_data/Account.yml +8 -0
  50. data/generators/crm/vendor/oracle_on_demand/spec_data/Contact.yml +8 -0
  51. data/generators/crm/vendor/oracle_on_demand/spec_data/GenericObject.yml +4 -0
  52. data/generators/crm/vendor/oracle_on_demand/spec_data/Lead.yml +14 -0
  53. data/generators/crm/vendor/oracle_on_demand/spec_data/Opportunity.yml +6 -0
  54. data/generators/crm/vendor/oracle_on_demand/templates.rb +52 -0
  55. data/generators/crm/vendor/salesforce/adapter.rb +315 -0
  56. data/generators/crm/vendor/salesforce/application.rb +80 -0
  57. data/generators/crm/vendor/salesforce/settings/Account.yml +53 -0
  58. data/generators/crm/vendor/salesforce/settings/Contact.yml +61 -0
  59. data/generators/crm/vendor/salesforce/settings/GenericObject.yml +13 -0
  60. data/generators/crm/vendor/salesforce/settings/Lead.yml +73 -0
  61. data/generators/crm/vendor/salesforce/settings/Opportunity.yml +48 -0
  62. data/generators/crm/vendor/salesforce/settings/settings.yml +6 -0
  63. data/generators/crm/vendor/salesforce/spec/application_spec.rb +14 -0
  64. data/generators/crm/vendor/salesforce/spec/sources/account_spec.rb +50 -0
  65. data/generators/crm/vendor/salesforce/spec/sources/contact_spec.rb +50 -0
  66. data/generators/crm/vendor/salesforce/spec/sources/lead_spec.rb +51 -0
  67. data/generators/crm/vendor/salesforce/spec/sources/opportunity_spec.rb +51 -0
  68. data/generators/crm/vendor/salesforce/spec_data/Account.yml +14 -0
  69. data/generators/crm/vendor/salesforce/spec_data/Contact.yml +8 -0
  70. data/generators/crm/vendor/salesforce/spec_data/GenericObject.yml +3 -0
  71. data/generators/crm/vendor/salesforce/spec_data/Lead.yml +10 -0
  72. data/generators/crm/vendor/salesforce/spec_data/Opportunity.yml +10 -0
  73. data/generators/crm/vendor/salesforce/templates.rb +45 -0
  74. data/generators/crm/vendor/sugar/adapter.rb +291 -0
  75. data/generators/crm/vendor/sugar/application.rb +50 -0
  76. data/generators/crm/vendor/sugar/settings/Account.yml +49 -0
  77. data/generators/crm/vendor/sugar/settings/Contact.yml +62 -0
  78. data/generators/crm/vendor/sugar/settings/GenericObject.yml +12 -0
  79. data/generators/crm/vendor/sugar/settings/Lead.yml +76 -0
  80. data/generators/crm/vendor/sugar/settings/Opportunity.yml +49 -0
  81. data/generators/crm/vendor/sugar/settings/settings.yml +9 -0
  82. data/generators/crm/vendor/sugar/spec/application_spec.rb +25 -0
  83. data/generators/crm/vendor/sugar/spec/sources/account_spec.rb +53 -0
  84. data/generators/crm/vendor/sugar/spec/sources/contact_spec.rb +53 -0
  85. data/generators/crm/vendor/sugar/spec/sources/lead_spec.rb +54 -0
  86. data/generators/crm/vendor/sugar/spec/sources/opportunity_spec.rb +54 -0
  87. data/generators/crm/vendor/sugar/spec_data/Account.yml +13 -0
  88. data/generators/crm/vendor/sugar/spec_data/Contact.yml +8 -0
  89. data/generators/crm/vendor/sugar/spec_data/GenericObject.yml +3 -0
  90. data/generators/crm/vendor/sugar/spec_data/Lead.yml +16 -0
  91. data/generators/crm/vendor/sugar/spec_data/Opportunity.yml +10 -0
  92. data/generators/crm/vendor/sugar/sugar.rb +33 -0
  93. data/generators/crm/vendor/sugar/templates.rb +58 -0
  94. data/generators/rhoconnect-adapters.rb +217 -0
  95. data/lib/rhoconnect-adapters/crm/crm.rb +31 -0
  96. data/lib/rhoconnect-adapters/soap_service.rb +70 -0
  97. data/lib/rhoconnect-adapters/version.rb +3 -0
  98. data/lib/rhoconnect-adapters.rb +2 -0
  99. data/rhoconnect-adapters.gemspec +36 -0
  100. data/spec/apps/ms_dynamics_spec.rb +19 -0
  101. data/spec/apps/oracle_on_demand_spec.rb +20 -0
  102. data/spec/apps/salesforce_spec.rb +18 -0
  103. data/spec/apps/sugar_spec.rb +18 -0
  104. data/spec/generator/generator_spec.rb +113 -0
  105. data/spec/spec_helper.rb +57 -0
  106. 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
+