dynamics_crm 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +15 -0
- data/.gitignore +18 -0
- data/.rspec +1 -0
- data/.travis.yml +9 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +104 -0
- data/Rakefile +9 -0
- data/dynamics_crm.gemspec +28 -0
- data/lib/dynamics_crm/client.rb +295 -0
- data/lib/dynamics_crm/metadata/attribute_metadata.rb +42 -0
- data/lib/dynamics_crm/metadata/entity_metadata.rb +24 -0
- data/lib/dynamics_crm/metadata/retrieve_all_entities_response.rb +21 -0
- data/lib/dynamics_crm/metadata/retrieve_attribute_response.rb +16 -0
- data/lib/dynamics_crm/metadata/retrieve_entity_response.rb +17 -0
- data/lib/dynamics_crm/metadata/xml_document.rb +39 -0
- data/lib/dynamics_crm/response/create_result.rb +15 -0
- data/lib/dynamics_crm/response/execute_result.rb +26 -0
- data/lib/dynamics_crm/response/result.rb +78 -0
- data/lib/dynamics_crm/response/retrieve_multiple_result.rb +38 -0
- data/lib/dynamics_crm/response/retrieve_result.rb +20 -0
- data/lib/dynamics_crm/version.rb +3 -0
- data/lib/dynamics_crm/xml/attributes.rb +163 -0
- data/lib/dynamics_crm/xml/column_set.rb +34 -0
- data/lib/dynamics_crm/xml/criteria.rb +54 -0
- data/lib/dynamics_crm/xml/entity.rb +61 -0
- data/lib/dynamics_crm/xml/entity_reference.rb +56 -0
- data/lib/dynamics_crm/xml/fault.rb +42 -0
- data/lib/dynamics_crm/xml/fetch_expression.rb +27 -0
- data/lib/dynamics_crm/xml/message_builder.rb +222 -0
- data/lib/dynamics_crm/xml/message_parser.rb +68 -0
- data/lib/dynamics_crm/xml/orders.rb +36 -0
- data/lib/dynamics_crm/xml/page_info.rb +38 -0
- data/lib/dynamics_crm/xml/query.rb +38 -0
- data/lib/dynamics_crm.rb +46 -0
- data/spec/fixtures/associate_response.xml +17 -0
- data/spec/fixtures/create_response.xml +19 -0
- data/spec/fixtures/delete_response.xml +16 -0
- data/spec/fixtures/disassociate_response.xml +17 -0
- data/spec/fixtures/fetch_xml_response.xml +120 -0
- data/spec/fixtures/receiver_fault.xml +27 -0
- data/spec/fixtures/request_security_token_response.xml +62 -0
- data/spec/fixtures/retrieve_account_all_columns.xml +402 -0
- data/spec/fixtures/retrieve_all_entities.xml +614 -0
- data/spec/fixtures/retrieve_attribute_identifier_response.xml +117 -0
- data/spec/fixtures/retrieve_attribute_picklist_response.xml +1097 -0
- data/spec/fixtures/retrieve_attribute_response.xml +126 -0
- data/spec/fixtures/retrieve_entity_response.xml +671 -0
- data/spec/fixtures/retrieve_multiple_result.xml +67 -0
- data/spec/fixtures/sender_fault.xml +34 -0
- data/spec/fixtures/update_response.xml +16 -0
- data/spec/fixtures/who_am_i_result.xml +35 -0
- data/spec/lib/client_spec.rb +230 -0
- data/spec/lib/metadata/entity_metadata_spec.rb +24 -0
- data/spec/lib/metadata/retrieve_all_entities_response_spec.rb +26 -0
- data/spec/lib/metadata/retrieve_attribute_response_spec.rb +65 -0
- data/spec/lib/metadata/retrieve_entity_response_spec.rb +24 -0
- data/spec/lib/response/execute_result_spec.rb +20 -0
- data/spec/lib/response/retrieve_multiple_spec.rb +34 -0
- data/spec/lib/response/retrieve_result_spec.rb +67 -0
- data/spec/lib/xml/attributes_spec.rb +39 -0
- data/spec/lib/xml/column_set_spec.rb +19 -0
- data/spec/lib/xml/entity_reference_spec.rb +47 -0
- data/spec/lib/xml/entity_spec.rb +63 -0
- data/spec/lib/xml/fault_spec.rb +41 -0
- data/spec/lib/xml/query_spec.rb +43 -0
- data/spec/spec_helper.rb +17 -0
- data/spec/support/fixture_helpers.rb +14 -0
- metadata +240 -0
checksums.yaml
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
---
|
2
|
+
!binary "U0hBMQ==":
|
3
|
+
metadata.gz: !binary |-
|
4
|
+
MDYyNmExNWY0NTlhOGZmZTA2NzMyYWUzM2U2MjFlNjM2MWIxZGRiNQ==
|
5
|
+
data.tar.gz: !binary |-
|
6
|
+
NmNiMjM2OGMxNGYxMTcyNmE2MTg5YjUxMjkwZmQ2Yzc5ZWFkNGY4OQ==
|
7
|
+
SHA512:
|
8
|
+
metadata.gz: !binary |-
|
9
|
+
ZDJkMTRhM2YxZjgxYmJmZWRkODlhZjE3YzZhOTQzNjZhNTJhNzgwZTk5NzE5
|
10
|
+
Mzk1MjJmZTIxZThjYjQyN2MwYTAwZmI2YTVhMTQyYzVkNjU0M2FlOTAyMjAw
|
11
|
+
MTc4NjQ5MjM1YzE1YTg2ZmFhODYwN2M1MzBjODZiNzdmNmM2NjQ=
|
12
|
+
data.tar.gz: !binary |-
|
13
|
+
MjgyOGVjMzA5ZDI2YzI3MGFkYzdkN2EwZjg5OTJiZjJhMzAwMmVmYTBjM2U1
|
14
|
+
ZTAwMjM3Mjg2MThiMzRmMjk1N2NlZWFhOTkwY2Q5MmE5N2YzZjliMjA1MWVj
|
15
|
+
OTYwOGUwNjc2OTdmMjQ3MjIxZGI1YjA4MTM2NTYxZDlhNWYyNmI=
|
data/.gitignore
ADDED
data/.rspec
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
--color
|
data/.travis.yml
ADDED
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2014 Joe Heth
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,104 @@
|
|
1
|
+
# DynamicsCRM
|
2
|
+
|
3
|
+
[![Build Status](https://travis-ci.org/TinderBox/dynamics_crm.png)](https://travis-ci.org/TinderBox/dynamics_crm)
|
4
|
+
|
5
|
+
Ruby library for accessing Microsoft Dynamics CRM Online 2011/2013 via their SOAP API.
|
6
|
+
|
7
|
+
## Installation
|
8
|
+
|
9
|
+
Add this line to your application's Gemfile:
|
10
|
+
|
11
|
+
gem 'dynamics_crm'
|
12
|
+
|
13
|
+
And then execute:
|
14
|
+
|
15
|
+
$ bundle
|
16
|
+
|
17
|
+
Or install it yourself as:
|
18
|
+
|
19
|
+
$ gem install dynamics_crm
|
20
|
+
|
21
|
+
## Usage
|
22
|
+
|
23
|
+
|
24
|
+
#### Username/Password authentication
|
25
|
+
|
26
|
+
```ruby
|
27
|
+
client = DynamicsCRM::Client.new
|
28
|
+
client.authenticate('user@orgname.onmicrosoft.com', 'password')
|
29
|
+
```
|
30
|
+
|
31
|
+
### retrieve
|
32
|
+
|
33
|
+
```ruby
|
34
|
+
client.retrieve('account', '53291AAB-4A9A-E311-B097-6C3BE5A8DD60')
|
35
|
+
# => #<DynamicsCRM::XML::Entity ... >
|
36
|
+
```
|
37
|
+
|
38
|
+
### retrieve_multiple
|
39
|
+
|
40
|
+
```ruby
|
41
|
+
client.retrieve_multiple('account', ["name", "Equal", "Test Account"])
|
42
|
+
# => [#<DynamicsCRM::XML::Entity ... >]
|
43
|
+
|
44
|
+
client.retrieve_multiple('account', ["name", "Equal", "Test Account"], ["Name, "CreatedBy"])
|
45
|
+
# => [#<DynamicsCRM::XML::Entity ... >]
|
46
|
+
```
|
47
|
+
|
48
|
+
|
49
|
+
### create
|
50
|
+
|
51
|
+
```ruby
|
52
|
+
# Add a new account
|
53
|
+
client.create('Account', name: 'Foobar Inc.')
|
54
|
+
# => {id: '53291AAB-4A9A-E311-B097-6C3BE5A8DD60'}
|
55
|
+
```
|
56
|
+
|
57
|
+
### update
|
58
|
+
|
59
|
+
```ruby
|
60
|
+
# Update the Account with id '53291AAB-4A9A-E311-B097-6C3BE5A8DD60'
|
61
|
+
client.update('account', '53291AAB-4A9A-E311-B097-6C3BE5A8DD60', name: 'Whizbang Corp')
|
62
|
+
# => {}
|
63
|
+
```
|
64
|
+
|
65
|
+
### delete
|
66
|
+
|
67
|
+
```ruby
|
68
|
+
# Delete the Account with id '53291AAB-4A9A-E311-B097-6C3BE5A8DD60'
|
69
|
+
client.delete('account', '53291AAB-4A9A-E311-B097-6C3BE5A8DD60')
|
70
|
+
# => {}
|
71
|
+
```
|
72
|
+
|
73
|
+
### retrieve_all_entities
|
74
|
+
|
75
|
+
```ruby
|
76
|
+
# get the list of organization entities
|
77
|
+
client.retrieve_all_entities
|
78
|
+
# => [#<DynamicsCRM::Metadata::EntityMetadata>, ...]
|
79
|
+
```
|
80
|
+
|
81
|
+
### retrieve_entity
|
82
|
+
|
83
|
+
```ruby
|
84
|
+
# get the entity metadata for the account object
|
85
|
+
client.retrieve_entity('account')
|
86
|
+
# => DynamicsCRM::Metadata::EntityMetadata
|
87
|
+
```
|
88
|
+
|
89
|
+
### retrieve_attribute
|
90
|
+
|
91
|
+
```ruby
|
92
|
+
# get AttributeMetadata for 'name' field on the account object
|
93
|
+
client.retrieve_attribute('account', 'name')
|
94
|
+
# => [#<DynamicsCRM::Metadata::AttributeMetadata>, ...]
|
95
|
+
```
|
96
|
+
|
97
|
+
|
98
|
+
## Contributing
|
99
|
+
|
100
|
+
1. Fork it
|
101
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
102
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
103
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
104
|
+
5. Create new Pull Request
|
data/Rakefile
ADDED
@@ -0,0 +1,28 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'dynamics_crm/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "dynamics_crm"
|
8
|
+
spec.version = DynamicsCRM::VERSION
|
9
|
+
spec.authors = ["Joe Heth"]
|
10
|
+
spec.email = ["joeheth@gmail.com"]
|
11
|
+
spec.description = %q{Ruby API for integrating with MS Dynamics 2011/2013 SOAP API}
|
12
|
+
spec.summary = %q{Ruby gem for integrating with MS Dynamics 2011/2013 SOAP API}
|
13
|
+
spec.homepage = "https://github.com/TinderBox/dynamics_crm"
|
14
|
+
spec.license = "MIT"
|
15
|
+
|
16
|
+
spec.files = `git ls-files`.split($/)
|
17
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
18
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
19
|
+
spec.require_paths = ["lib"]
|
20
|
+
|
21
|
+
spec.add_runtime_dependency 'curb', '~> 0.8', '>= 0.8.5'
|
22
|
+
spec.add_runtime_dependency 'mimemagic', '~> 0.2', '>= 0.2.1'
|
23
|
+
|
24
|
+
spec.add_development_dependency "bundler", "~> 1.3"
|
25
|
+
spec.add_development_dependency 'rake', '~> 10.1'
|
26
|
+
spec.add_development_dependency 'rspec', '~> 2.14'
|
27
|
+
spec.add_development_dependency 'simplecov', '~> 0.7'
|
28
|
+
end
|
@@ -0,0 +1,295 @@
|
|
1
|
+
# SOAP Only Walk through
|
2
|
+
# http://code.msdn.microsoft.com/CRM-Online-2011-WebServices-14913a16
|
3
|
+
#
|
4
|
+
# PHP starting point:
|
5
|
+
# http://crmtroubleshoot.blogspot.com.au/2013/07/dynamics-crm-2011-php-and-soap-using.html
|
6
|
+
#
|
7
|
+
# OCP: Open Commerce Platform
|
8
|
+
# https://community.dynamics.com/crm/b/crmgirishraja/archive/2012/09/04/authentication-with-dynamics-crm-online-on-ocp-office-365.aspx
|
9
|
+
module DynamicsCRM
|
10
|
+
|
11
|
+
class Client
|
12
|
+
extend Forwardable
|
13
|
+
include XML::MessageBuilder
|
14
|
+
|
15
|
+
# The Login URL and Region are located in the client's Organization WSDL.
|
16
|
+
# https://tinderboxdev.api.crm.dynamics.com/XRMServices/2011/Organization.svc?wsdl=wsdl0
|
17
|
+
#
|
18
|
+
# Login URL: Policy -> Issuer -> Address
|
19
|
+
# Region: SecureTokenService -> AppliesTo
|
20
|
+
LOGIN_URL = "https://login.microsoftonline.com/RST2.srf"
|
21
|
+
REGION = 'urn:crmna:dynamics.com'
|
22
|
+
|
23
|
+
attr_accessor :logger, :caller_id
|
24
|
+
|
25
|
+
# Initializes Client instance.
|
26
|
+
# Requires: organization_name
|
27
|
+
# Optional: hostname
|
28
|
+
def initialize(config={organization_name: nil, hostname: nil, caller_id: nil})
|
29
|
+
raise RuntimeError.new("organization_name is required") if config[:organization_name].nil?
|
30
|
+
|
31
|
+
@organization_name = config[:organization_name]
|
32
|
+
@hostname = config[:hostname] || "#{@organization_name}.api.crm.dynamics.com"
|
33
|
+
@organization_endpoint = "https://#{@hostname}/XRMServices/2011/Organization.svc"
|
34
|
+
@caller_id = config[:caller_id]
|
35
|
+
end
|
36
|
+
|
37
|
+
# Public: Authenticate User
|
38
|
+
#
|
39
|
+
# Examples
|
40
|
+
#
|
41
|
+
# client.authenticate('test@orgnam.onmicrosoft.com', 'password')
|
42
|
+
# # => true || raised Fault
|
43
|
+
#
|
44
|
+
# Returns true on success or raises Fault
|
45
|
+
def authenticate(username, password)
|
46
|
+
|
47
|
+
@username = username
|
48
|
+
@password = password
|
49
|
+
|
50
|
+
soap_response = post(LOGIN_URL, build_ocp_request(username, password))
|
51
|
+
|
52
|
+
document = REXML::Document.new(soap_response)
|
53
|
+
# Check for Fault
|
54
|
+
fault_xml = document.get_elements("//[local-name() = 'Fault']")
|
55
|
+
raise XML::Fault.new(fault_xml) if fault_xml.any?
|
56
|
+
|
57
|
+
cipher_values = document.get_elements("//CipherValue")
|
58
|
+
|
59
|
+
if cipher_values && cipher_values.length > 0
|
60
|
+
@security_token0 = cipher_values[0].text
|
61
|
+
@security_token1 = cipher_values[1].text
|
62
|
+
# Use local-name() to ignore namespace.
|
63
|
+
@key_identifier = document.get_elements("//[local-name() = 'KeyIdentifier']").first.text
|
64
|
+
else
|
65
|
+
raise RuntimeError.new(soap_response)
|
66
|
+
end
|
67
|
+
|
68
|
+
true
|
69
|
+
end
|
70
|
+
|
71
|
+
# These are all the operations defined by the Dynamics WSDL.
|
72
|
+
# Tag names are case-sensitive.
|
73
|
+
def create(entity_name, attributes)
|
74
|
+
|
75
|
+
entity = XML::Entity.new(entity_name)
|
76
|
+
entity.attributes = XML::Attributes.new(attributes)
|
77
|
+
|
78
|
+
xml_response = post(@organization_endpoint, create_request(entity))
|
79
|
+
return Response::CreateResult.new(xml_response)
|
80
|
+
end
|
81
|
+
|
82
|
+
# http://crmtroubleshoot.blogspot.com.au/2013/07/dynamics-crm-2011-php-and-soap-calls.html
|
83
|
+
def retrieve(entity_name, guid, columns=[])
|
84
|
+
|
85
|
+
column_set = XML::ColumnSet.new(columns)
|
86
|
+
request = retrieve_request(entity_name, guid, column_set)
|
87
|
+
|
88
|
+
xml_response = post(@organization_endpoint, request)
|
89
|
+
return Response::RetrieveResult.new(xml_response)
|
90
|
+
end
|
91
|
+
|
92
|
+
def rollup(target_entity, query, rollup_type="Related")
|
93
|
+
self.execute("Rollup", {
|
94
|
+
Target: target_entity,
|
95
|
+
Query: query,
|
96
|
+
RollupType: rollup_type
|
97
|
+
})
|
98
|
+
end
|
99
|
+
|
100
|
+
def retrieve_multiple(entity_name, criteria=[], columns=[])
|
101
|
+
|
102
|
+
query = XML::Query.new(entity_name)
|
103
|
+
query.columns = columns
|
104
|
+
query.criteria = XML::Criteria.new(criteria)
|
105
|
+
|
106
|
+
request = retrieve_multiple_request(query)
|
107
|
+
xml_response = post(@organization_endpoint, request)
|
108
|
+
return Response::RetrieveMultipleResult.new(xml_response)
|
109
|
+
end
|
110
|
+
|
111
|
+
def fetch(fetchxml)
|
112
|
+
response = self.execute("RetrieveMultiple", {
|
113
|
+
Query: XML::FetchExpression.new(fetchxml)
|
114
|
+
})
|
115
|
+
end
|
116
|
+
|
117
|
+
# Update entity attributes
|
118
|
+
def update(entity_name, guid, attributes)
|
119
|
+
|
120
|
+
entity = XML::Entity.new(entity_name)
|
121
|
+
entity.id = guid
|
122
|
+
entity.attributes = XML::Attributes.new(attributes)
|
123
|
+
|
124
|
+
request = update_request(entity)
|
125
|
+
xml_response = post(@organization_endpoint, request)
|
126
|
+
return Response::UpdateResponse.new(xml_response)
|
127
|
+
end
|
128
|
+
|
129
|
+
def delete(entity_name, guid)
|
130
|
+
request = delete_request(entity_name, guid)
|
131
|
+
|
132
|
+
xml_response = post(@organization_endpoint, request)
|
133
|
+
return Response::DeleteResponse.new(xml_response)
|
134
|
+
end
|
135
|
+
|
136
|
+
def execute(action, parameters={}, response_class=nil)
|
137
|
+
request = execute_request(action, parameters)
|
138
|
+
xml_response = post(@organization_endpoint, request)
|
139
|
+
|
140
|
+
response_class ||= Response::ExecuteResult
|
141
|
+
return response_class.new(xml_response)
|
142
|
+
end
|
143
|
+
|
144
|
+
def associate(entity_name, guid, relationship, related_entities)
|
145
|
+
request = associate_request(entity_name, guid, relationship, related_entities)
|
146
|
+
xml_response = post(@organization_endpoint, request)
|
147
|
+
return Response::AssociateResponse.new(xml_response)
|
148
|
+
end
|
149
|
+
|
150
|
+
def disassociate(entity_name, guid, relationship, related_entities)
|
151
|
+
request = disassociate_request(entity_name, guid, relationship, related_entities)
|
152
|
+
xml_response = post(@organization_endpoint, request)
|
153
|
+
return Response::DisassociateResponse.new(xml_response)
|
154
|
+
end
|
155
|
+
|
156
|
+
def create_attachment(entity_name, entity_id, options={})
|
157
|
+
raise "options must contain a document entry" unless options[:document]
|
158
|
+
|
159
|
+
file_name = options[:filename]
|
160
|
+
document = options[:document]
|
161
|
+
subject = options[:subject]
|
162
|
+
text = options[:text] || ""
|
163
|
+
|
164
|
+
if document.is_a?(String) && File.exists?(document)
|
165
|
+
file = File.new(document)
|
166
|
+
elsif document.is_a?(String) && document.start_with?("http")
|
167
|
+
require 'open-uri'
|
168
|
+
file = open(document)
|
169
|
+
else
|
170
|
+
file = document
|
171
|
+
end
|
172
|
+
|
173
|
+
if file.respond_to?(:base_uri)
|
174
|
+
file_name ||= File.basename(file.base_uri.path)
|
175
|
+
mime_type = MimeMagic.by_path(file.base_uri.path)
|
176
|
+
elsif file.respond_to?(:path)
|
177
|
+
file_name ||= File.basename(file.path)
|
178
|
+
mime_type = MimeMagic.by_path(file.path)
|
179
|
+
else
|
180
|
+
raise "file must be a valid File object, file path or URL"
|
181
|
+
end
|
182
|
+
|
183
|
+
documentbody = file.read
|
184
|
+
attributes = {
|
185
|
+
objectid: {id: entity_id, logical_name: entity_name},
|
186
|
+
subject: subject || file_name,
|
187
|
+
notetext: text || "",
|
188
|
+
filename: file_name,
|
189
|
+
isdocument: true,
|
190
|
+
documentbody: ::Base64.encode64(documentbody),
|
191
|
+
filesize: documentbody.length,
|
192
|
+
mimetype: mime_type
|
193
|
+
}
|
194
|
+
|
195
|
+
self.create("annotation", attributes)
|
196
|
+
end
|
197
|
+
|
198
|
+
def retrieve_attachments(entity_id, columns=["filename", "documentbody", "mimetype"])
|
199
|
+
self.retrieve_multiple("annotation", [["objectid", "Equal", entity_id], ["isdocument", "Equal", true]], columns)
|
200
|
+
end
|
201
|
+
|
202
|
+
# Metadata Calls
|
203
|
+
# EntityFilters Enum: Default, Entity, Attributes, Privileges, Relationships, All
|
204
|
+
def retrieve_all_entities
|
205
|
+
response = self.execute("RetrieveAllEntities", {
|
206
|
+
EntityFilters: "Entity",
|
207
|
+
RetrieveAsIfPublished: true
|
208
|
+
},
|
209
|
+
Metadata::RetrieveAllEntitiesResponse)
|
210
|
+
end
|
211
|
+
|
212
|
+
# EntityFilters Enum: Default, Entity, Attributes, Privileges, Relationships, All
|
213
|
+
def retrieve_entity(logical_name, entity_filter="Attributes")
|
214
|
+
self.execute("RetrieveEntity", {
|
215
|
+
LogicalName: logical_name,
|
216
|
+
MetadataId: "00000000-0000-0000-0000-000000000000",
|
217
|
+
EntityFilters: entity_filter,
|
218
|
+
RetrieveAsIfPublished: true
|
219
|
+
},
|
220
|
+
Metadata::RetrieveEntityResponse)
|
221
|
+
end
|
222
|
+
|
223
|
+
def retrieve_attribute(entity_logical_name, logical_name)
|
224
|
+
self.execute("RetrieveAttribute", {
|
225
|
+
EntityLogicalName: entity_logical_name,
|
226
|
+
LogicalName: logical_name,
|
227
|
+
MetadataId: "00000000-0000-0000-0000-000000000000",
|
228
|
+
RetrieveAsIfPublished: true
|
229
|
+
},
|
230
|
+
Metadata::RetrieveAttributeResponse)
|
231
|
+
end
|
232
|
+
|
233
|
+
def who_am_i
|
234
|
+
self.execute('WhoAmI')
|
235
|
+
end
|
236
|
+
|
237
|
+
def load_entity(logical_name, id)
|
238
|
+
case logical_name
|
239
|
+
when "opportunity"
|
240
|
+
Model::Opportunity.new(id, self)
|
241
|
+
else
|
242
|
+
Model::Entity.new(logical_name, id, self)
|
243
|
+
end
|
244
|
+
end
|
245
|
+
|
246
|
+
protected
|
247
|
+
|
248
|
+
def post(url, request)
|
249
|
+
|
250
|
+
log_xml("REQUEST", request)
|
251
|
+
|
252
|
+
c = Curl::Easy.new(url) do |http|
|
253
|
+
# Set up headers.
|
254
|
+
http.headers["Connection"] = "Keep-Alive"
|
255
|
+
http.headers["Content-type"] = "application/soap+xml; charset=UTF-8"
|
256
|
+
http.headers["Content-length"] = request.length
|
257
|
+
|
258
|
+
http.ssl_verify_peer = false
|
259
|
+
http.timeout = 60
|
260
|
+
http.follow_location = true
|
261
|
+
http.ssl_version = 3
|
262
|
+
# http.verbose = 1
|
263
|
+
end
|
264
|
+
|
265
|
+
if c.http_post(request)
|
266
|
+
response = c.body_str
|
267
|
+
else
|
268
|
+
|
269
|
+
end
|
270
|
+
|
271
|
+
log_xml("RESPONSE", response)
|
272
|
+
|
273
|
+
response
|
274
|
+
end
|
275
|
+
|
276
|
+
def log_xml(title, xml)
|
277
|
+
return unless logger
|
278
|
+
|
279
|
+
logger.puts(title)
|
280
|
+
doc = REXML::Document.new(xml)
|
281
|
+
formatter.write(doc.root, logger)
|
282
|
+
logger.puts
|
283
|
+
end
|
284
|
+
|
285
|
+
def formatter
|
286
|
+
unless @formatter
|
287
|
+
@formatter = REXML::Formatters::Pretty.new(2)
|
288
|
+
@formatter.compact = true # This is the magic line that does what you need!
|
289
|
+
end
|
290
|
+
@formatter
|
291
|
+
end
|
292
|
+
|
293
|
+
end
|
294
|
+
|
295
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
module DynamicsCRM
|
2
|
+
module Metadata
|
3
|
+
|
4
|
+
# AttributeMetadata
|
5
|
+
# ManagedPropertyAttributeMetadata
|
6
|
+
# IntegerAttributeMetadata
|
7
|
+
# BooleanAttributeMetadata
|
8
|
+
# DateTimeAttributeMetadata
|
9
|
+
# DecimalAttributeMetadata
|
10
|
+
# DoubleAttributeMetadata
|
11
|
+
# EntityNameAttributeMetadata
|
12
|
+
# MoneyAttributeMetadata
|
13
|
+
# StringAttributeMetadata
|
14
|
+
# LookupAttributeMetadata
|
15
|
+
# MemoAttributeMetadata
|
16
|
+
# BigIntAttributeMetadata
|
17
|
+
# PicklistAttributeMetadata
|
18
|
+
# StateAttributeMetadata
|
19
|
+
# StatusAttributeMetadata
|
20
|
+
#
|
21
|
+
# http://msdn.microsoft.com/en-us/library/microsoft.xrm.sdk.metadata.attributemetadata.aspx
|
22
|
+
class AttributeMetadata < XmlDocument
|
23
|
+
|
24
|
+
# Only applicable to PicklistAttributeMetadata
|
25
|
+
def picklist_options
|
26
|
+
return @picklist_options if @picklist_options
|
27
|
+
|
28
|
+
@picklist_options = {}
|
29
|
+
option_metadata = "./d:OptionSet/d:Options/d:OptionMetadata"
|
30
|
+
@document.get_elements(option_metadata).each do |option|
|
31
|
+
numeric_value = option.elements["d:Value"].text
|
32
|
+
label = option.elements["d:Label/b:UserLocalizedLabel/b:Label"].text
|
33
|
+
@picklist_options[numeric_value.to_i] = label
|
34
|
+
end
|
35
|
+
|
36
|
+
@picklist_options
|
37
|
+
end
|
38
|
+
|
39
|
+
end
|
40
|
+
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
module DynamicsCRM
|
2
|
+
module Metadata
|
3
|
+
# Represents EntityMetdata XML fragment.
|
4
|
+
# Optionally contains list of AttributeMetadata
|
5
|
+
class EntityMetadata < XmlDocument
|
6
|
+
attr_reader :attributes
|
7
|
+
|
8
|
+
def initialize(document)
|
9
|
+
super
|
10
|
+
end
|
11
|
+
|
12
|
+
def attributes
|
13
|
+
return if @attributes.is_a?(Array)
|
14
|
+
|
15
|
+
@attributes = document.get_elements("//d:Attributes/d:AttributeMetadata").collect do |attr_metadata|
|
16
|
+
AttributeMetadata.new(attr_metadata)
|
17
|
+
end
|
18
|
+
|
19
|
+
return @attributes
|
20
|
+
end
|
21
|
+
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module DynamicsCRM
|
2
|
+
module Metadata
|
3
|
+
|
4
|
+
# Retrieve All Entities returns a list of EntityMetadata
|
5
|
+
class RetrieveAllEntitiesResponse < DynamicsCRM::Response::ExecuteResult
|
6
|
+
attr_reader :entities
|
7
|
+
|
8
|
+
def initialize(xml)
|
9
|
+
super
|
10
|
+
@entities = []
|
11
|
+
# Single KeyValuePair of EntityMetadata -> [EntityMetdata,...]
|
12
|
+
self.delete("EntityMetadata").each do |em_xml|
|
13
|
+
@entities << EntityMetadata.new(em_xml)
|
14
|
+
end
|
15
|
+
|
16
|
+
end
|
17
|
+
|
18
|
+
end
|
19
|
+
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
module DynamicsCRM
|
2
|
+
module Metadata
|
3
|
+
# Retrieve Attribute returns a single AttributeMetadata.
|
4
|
+
class RetrieveAttributeResponse < DynamicsCRM::Response::ExecuteResult
|
5
|
+
attr_reader :attribute
|
6
|
+
|
7
|
+
def initialize(xml)
|
8
|
+
super
|
9
|
+
|
10
|
+
# Single KeyValuePair containing 1 value type of AttributeMetadata
|
11
|
+
@attribute = AttributeMetadata.new(self.delete("AttributeMetadata"))
|
12
|
+
end
|
13
|
+
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
module DynamicsCRM
|
2
|
+
module Metadata
|
3
|
+
|
4
|
+
# RetrieveEntity returns a single EntityMetadata element.
|
5
|
+
class RetrieveEntityResponse < DynamicsCRM::Response::ExecuteResult
|
6
|
+
attr_reader :entity, :attributes
|
7
|
+
|
8
|
+
def initialize(xml)
|
9
|
+
super
|
10
|
+
|
11
|
+
# Single KeyValuePair containing 1 value type of EntityMetadata
|
12
|
+
@entity = EntityMetadata.new(self["EntityMetadata"])
|
13
|
+
end
|
14
|
+
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
module DynamicsCRM
|
2
|
+
module Metadata
|
3
|
+
|
4
|
+
class XmlDocument
|
5
|
+
|
6
|
+
attr_reader :document
|
7
|
+
def initialize(document)
|
8
|
+
@document = document
|
9
|
+
end
|
10
|
+
|
11
|
+
# Allows access to attributes in underlying XML document.
|
12
|
+
def method_missing(method, *args, &block)
|
13
|
+
value = nil
|
14
|
+
return value if @document.nil?
|
15
|
+
|
16
|
+
camel_name = method.to_s
|
17
|
+
element = @document.get_elements("./[local-name() = '#{camel_name}']").first
|
18
|
+
|
19
|
+
if element && element.children.size == 1 && element.children.first.is_a?(REXML::Text)
|
20
|
+
value = element.text
|
21
|
+
elsif element
|
22
|
+
value = XmlDocument.new(element)
|
23
|
+
else
|
24
|
+
# This returns if no XML element was found to avoid nil errors.
|
25
|
+
value = OpenStruct.new
|
26
|
+
end
|
27
|
+
|
28
|
+
# Return wrapper to support method_method missing.
|
29
|
+
return value
|
30
|
+
end
|
31
|
+
|
32
|
+
def respond_to_missing?(method_name, include_private = false)
|
33
|
+
camel_name = method_name.to_s
|
34
|
+
@document.get_elements("d:#{camel_name}").any? || super
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
end
|
39
|
+
end
|