landslider 0.1.0

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/CHANGELOG ADDED
File without changes
data/LICENSE ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2011, Jay Prall
2
+
3
+ Permission is hereby granted, free of charge, to any person
4
+ obtaining a copy of this software and associated documentation
5
+ files (the "Software"), to deal in the Software without
6
+ restriction, including without limitation the rights to use,
7
+ copy, modify, merge, publish, distribute, sublicense, and/or sell
8
+ copies of the Software, and to permit persons to whom the
9
+ Software is furnished to do so, subject to the following
10
+ conditions:
11
+
12
+ The above copyright notice and this permission notice shall be
13
+ included in all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
17
+ OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
19
+ HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
20
+ WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
21
+ FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
22
+ OTHER DEALINGS IN THE SOFTWARE.
data/README.rdoc ADDED
@@ -0,0 +1,39 @@
1
+ = Landslider
2
+
3
+ Ruby interface to Landslide's SOAP based API
4
+
5
+ == Pre-requisites
6
+
7
+ An account with Landslide that has the API enabled. (www.landslide.com)
8
+
9
+ == Resources
10
+
11
+ === Dependencies
12
+
13
+ This gem requires the following gems:
14
+ handsoap (1.1.8)
15
+
16
+ == Install
17
+
18
+ landslider requires the handsoap gem
19
+
20
+ gem install handsoap landslider
21
+
22
+ == Usage
23
+
24
+ Configuration:
25
+
26
+ # constants to be set by rails environment config files
27
+ LS_INSTANCE_NAME = 'jaytest'
28
+ LS_API_USERNAME = 'XXXXXXXX@landslide.com'
29
+ LS_API_PASSWORD = 'XXXXXXXX'
30
+ LS_API_KEY = Digest::MD5.hexdigest(LS_API_PASSWORD + LS_INSTANCE_NAME)
31
+ LS_API_NAMESPACE = "http://www.landslide.com/webservices/SoapService"
32
+ LS_API_ENDPOINT = {
33
+ :uri => "https://#{LS_INSTANCE_NAME}.api.landslide.com/webservices/SoapService",
34
+ :version => 2
35
+ }
36
+
37
+
38
+
39
+ more here later..
data/Rakefile ADDED
@@ -0,0 +1,20 @@
1
+ require 'rubygems'
2
+ require 'rake'
3
+
4
+ begin
5
+ require 'jeweler'
6
+ Jeweler::Tasks.new do |gemspec|
7
+ gemspec.name = "landslider"
8
+ gemspec.homepage = "https://github.com/j4y/landslider"
9
+ gemspec.summary = "Ruby interface to Landslide SOAP API"
10
+ gemspec.description = "Ruby interface to Landslide SOAP API. "
11
+ gemspec.email = "jayprall@gmail.com"
12
+ gemspec.authors = ['Jay Prall']
13
+ end
14
+ Jeweler::GemcutterTasks.new
15
+ rescue LoadError
16
+ puts "Jeweler not available. Please install the jeweler gem"
17
+ end
18
+
19
+ # Load any rake files that exist
20
+ Dir["#{File.dirname(__FILE__)}/tasks/*.rake"].sort.each { |f| load f }
data/VERSION.yml ADDED
@@ -0,0 +1,4 @@
1
+ ---
2
+ :patch: 0
3
+ :major: 0
4
+ :minor: 1
data/init.rb ADDED
@@ -0,0 +1,2 @@
1
+ # can be used as a rails plugin
2
+ require 'landslider'
@@ -0,0 +1,32 @@
1
+ # Generated by jeweler
2
+ # DO NOT EDIT THIS FILE DIRECTLY
3
+ # Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec'
4
+ # -*- encoding: utf-8 -*-
5
+
6
+ Gem::Specification.new do |s|
7
+ s.name = %q{landslider}
8
+ s.version = "0.1.0"
9
+
10
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
+ s.authors = ["Jay Prall"]
12
+ s.date = %q{2011-04-22}
13
+ s.description = %q{Ruby interface to Landslide SOAP API. }
14
+ s.email = %q{jayprall@gmail.com}
15
+ s.extra_rdoc_files = [
16
+ "LICENSE",
17
+ "README.rdoc"
18
+ ]
19
+ s.require_paths = ["lib"]
20
+ s.rubygems_version = %q{1.6.2}
21
+ s.summary = %q{Ruby interface to Landslide SOAP API}
22
+
23
+ if s.respond_to? :specification_version then
24
+ s.specification_version = 3
25
+
26
+ if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
27
+ else
28
+ end
29
+ else
30
+ end
31
+ end
32
+
data/lib/landslider.rb ADDED
@@ -0,0 +1,303 @@
1
+
2
+ require 'handsoap'
3
+ require 'curb'
4
+
5
+ class Landslider < Handsoap::Service
6
+
7
+ endpoint ::LS_API_ENDPOINT
8
+
9
+ def session_id
10
+ @session_id
11
+ end
12
+
13
+ def session_id=(session_id)
14
+ @session_id = session_id
15
+ end
16
+
17
+ def on_create_document(doc)
18
+ doc.alias 'urn', ::LS_API_NAMESPACE
19
+ header = doc.find('Header')
20
+ header.add('urn:SessionHeader') { |sh|
21
+ sh.add('urn:sessionId', self.session_id)
22
+ }
23
+ end
24
+
25
+ def on_http_error(response)
26
+ puts response.inspect
27
+ end
28
+
29
+ def login(session_id)
30
+ self.session_id = session_id
31
+
32
+ response = invoke("login", :soap_action => :none) do |message|
33
+ message.add('wsUser') { |u|
34
+ u.add 'username', ::LS_API_USERNAME
35
+ u.add 'password', ::LS_API_KEY
36
+ }
37
+ end
38
+
39
+ node = response.document.xpath('//ns:loginResponse/loginResponse', ns)
40
+ parse_login_result(node)
41
+ end
42
+
43
+ def get_api_version(session_id)
44
+ self.session_id = session_id
45
+
46
+ response = invoke("urn:getApiVersion", :soap_action => :none)
47
+
48
+ node = response.document.xpath('//ns:getApiVersionResponse', ns)
49
+ parse_api_version_result(node)
50
+ end
51
+
52
+ def get_accounts(session_id)
53
+ self.session_id = session_id
54
+ response = invoke("getAccounts", :soap_action => :none) do |message|
55
+ message.add('accountsRequest') { |ar|
56
+ ar.add 'firstResultPosition', 1
57
+ ar.add 'totalResultsRequested', 10
58
+ ar.add('searchCriteria') { |sc|
59
+ # just find accounts with an empty main city
60
+
61
+ sc.add 'fieldId', 'MainAddressCity'
62
+ sc.add 'operator', 'Empty'
63
+ }
64
+ }
65
+ end
66
+
67
+ node = response.document.xpath('//ns:getAccountsResponse', ns)
68
+ parse_get_accounts_result(node)
69
+ end
70
+
71
+ def get_account_opportunities(session_id, account_id)
72
+ self.session_id = session_id
73
+ response = invoke("getAccountOpportunities", :soap_action => :none) do |message|
74
+ message.add 'accountId', account_id
75
+ end
76
+ puts response.document.inspect
77
+
78
+ node = response.document.xpath('//ns:getAccountOpportunitiesResponse', ns)
79
+ parse_get_account_opportunities_result(node)
80
+ end
81
+
82
+
83
+
84
+ def get_account_contacts(session_id, account_id)
85
+ self.session_id = session_id
86
+ response = invoke("getAccountContacts", :soap_action => :none) do |message|
87
+ message.add 'accountId', account_id
88
+ end
89
+ puts response.document.inspect
90
+
91
+ node = response.document.xpath('//ns:getAccountContactsResponse', ns)
92
+ parse_get_account_contacts_result(node)
93
+ end
94
+
95
+ def get_account_notes(session_id, account_id)
96
+ self.session_id = session_id
97
+
98
+ response = invoke("getAccountNotes", :soap_action => :none) do |message|
99
+ message.add('accountNoteSearch') { |ans|
100
+
101
+ ans.add 'accountId', account_id
102
+ ans.add 'firstResultPosition', 1
103
+ ans.add 'totalResultsRequested', 10
104
+
105
+ # search criteria doesn't seem to work
106
+ # ans.add('searchCriteria') { |sc|
107
+ # sc.add 'fieldId', 'note'
108
+ # sc.add 'operator', 'Contains'
109
+ # sc.add 'queryValue', 'BLAH'
110
+ # }
111
+ }
112
+ end
113
+ puts response.document.inspect
114
+
115
+ end
116
+
117
+
118
+ def get_contact_notes(session_id, contact_id)
119
+ self.session_id = session_id
120
+
121
+ response = invoke("getContactNotes", :soap_action => :none) do |message|
122
+ message.add('contactNote') { |ans|
123
+ ans.add 'contactId', contact_id
124
+ ans.add 'firstResultPosition', 1
125
+ ans.add 'totalResultsRequested', 10
126
+ }
127
+ end
128
+ puts response.document.inspect
129
+
130
+ end
131
+
132
+
133
+ def get_opportunity_notes(session_id, opportunity_id)
134
+ self.session_id = session_id
135
+
136
+ response = invoke("getOpportunityNotes", :soap_action => :none) do |message|
137
+ message.add('opportunityNote') { |ans|
138
+
139
+ ans.add 'opportunityId', opportunity_id
140
+ ans.add 'firstResultPosition', 1
141
+ ans.add 'totalResultsRequested', 10
142
+ }
143
+ end
144
+ puts response.document.inspect
145
+
146
+ end
147
+
148
+ def get_lead_notes(session_id, lead_id)
149
+ self.session_id = session_id
150
+
151
+ response = invoke("getLeadNotes", :soap_action => :none) do |message|
152
+ message.add('leadNote') { |ans|
153
+
154
+ ans.add 'leadId', lead_id
155
+ ans.add 'firstResultPosition', 1
156
+ ans.add 'totalResultsRequested', 10
157
+ }
158
+ end
159
+ puts response.document.inspect
160
+
161
+ end
162
+
163
+
164
+ private
165
+
166
+ def ns
167
+ { 'ns' => LS_API_NAMESPACE }
168
+ end
169
+
170
+ def parse_login_result(node)
171
+ {
172
+ :error => xml_to_bool(node, './error/text()'),
173
+ :error_code => xml_to_int(node, './errorCode/text()'),
174
+ :result_msg => xml_to_str(node, './resultMsg/text()'),
175
+ :status_code => xml_to_int(node, './statusCode/text()'),
176
+ :password_expired => xml_to_bool(node, './passwordExpired/text()'),
177
+ :session_id => xml_to_str(node, './sessionId/text()')
178
+ }
179
+ end
180
+
181
+ def parse_api_version_result(node)
182
+ {
183
+ :internal_name => xml_to_str(node, '//ApiVersion/internalName/text()'),
184
+ :label => xml_to_str(node, '//ApiVersion/label/text()'),
185
+ :major_version => xml_to_int(node, '//ApiVersion/majorVersion/text()'),
186
+ :minor_version => xml_to_int(node, '//ApiVersion/minorVersion/text()'),
187
+ :revision_version => xml_to_int(node, '//ApiVersion/revisionVersion/text()')
188
+ }
189
+ end
190
+
191
+ def parse_get_accounts_result(node)
192
+ {
193
+ :accounts => node.xpath('//Accounts/accountList', ns).map { |child| parse_account(child) },
194
+ :error => xml_to_bool(node, '//Accounts/error/text()'),
195
+ :error_code => xml_to_int(node, '//Accounts/errorCode/text()'),
196
+ :result_msg => xml_to_str(node, '//Accounts/resultMsg/text()'),
197
+ :results_returned => xml_to_int(node, '//Accounts/resultsReturned/text()'),
198
+ :total_results_available => xml_to_int(node, '//Accounts/totalResultsAvailable/text()')
199
+ }
200
+ end
201
+
202
+ def parse_get_account_opportunities_result(node)
203
+ {
204
+ :opportunities => node.xpath('./*/opportunityList', ns).map { |child| parse_opportunity(child) },
205
+ :error => xml_to_bool(node, './*/error/text()'),
206
+ :results_returned => xml_to_int(node, './*/resultsReturned/text()'),
207
+ :total_results_available => xml_to_int(node, './*/totalResultsAvailable/text()')
208
+ }
209
+ end
210
+
211
+ def parse_get_account_contacts_result(node)
212
+ {
213
+ :contacts => node.xpath('./*/contactList', ns).map { |child| parse_contact(child) },
214
+ :error => xml_to_bool(node, './*/error/text()'),
215
+ :results_returned => xml_to_int(node, './*/resultsReturned/text()'),
216
+ :total_results_available => xml_to_int(node, './*/totalResultsAvailable/text()')
217
+ }
218
+ end
219
+
220
+ # WsAccount
221
+ def parse_account(node)
222
+ {
223
+ :entity_id => xml_to_str(node, './entityId/text()'),
224
+ :entity_type => xml_to_str(node, './entityType/text()'),
225
+ :parent_account_id => xml_to_str(node, './parentAccountId/text()'),
226
+ :account_id => xml_to_str(node, './accountId/text()'),
227
+ :account_name => xml_to_str(node, './accountName/text()'),
228
+ :account_owner => xml_to_str(node, './accountOwner/text()'),
229
+ :url => xml_to_str(node, './url/text()'),
230
+ :phone => xml_to_str(node, './phone/text()'),
231
+ :main_address => parse_address(node.xpath('./mainAddress')),
232
+ :shipping_address => parse_address(node.xpath('./shippingAddress')),
233
+ :billing_address => parse_address(node.xpath('./billingAddress')),
234
+ :updated_by => xml_to_int(node, './updatedBy/text()'),
235
+ :updated_on => xml_to_str(node, './updatedOn/text()'),
236
+ :created_by => xml_to_int(node, './createdBy/text()'),
237
+ :created_on => xml_to_str(node, './createdOn/text()'),
238
+ :archived_by => xml_to_str(node, './archivedBy/text()'),
239
+ :sync_with_quickbooks => xml_to_str(node, './isSyncWithQuickbooks/text()')
240
+ }
241
+
242
+ end
243
+
244
+ # WsAccountType
245
+ def parse_account_type(node)
246
+ {
247
+ #:entity_id => xml_to_str(node, './entityId/text()'),
248
+ #:entity_type => xml_to_str(node, './entityType/text()')
249
+ #:account_type_id => xml_to_str(node, './accountTypeId/text()'),
250
+ :account_type => xml_to_str(node, './accountType/text()')
251
+ }
252
+ end
253
+
254
+ # WsAddress
255
+ def parse_address(node)
256
+ {
257
+ #:entity_id => xml_to_str(node, './entityId/text()'),
258
+ #:entity_type => xml_to_str(node, './entityType/text()'),
259
+ #:address_id => xml_to_str(node, './addressId/text()'),
260
+ :address => xml_to_str(node, './address1/text()'),
261
+ :city => xml_to_str(node, './city/text()'),
262
+ :state => xml_to_str(node, './state/text()'),
263
+ :zip => xml_to_str(node, './zipPostal/text()'),
264
+ :country => xml_to_str(node, './country/text()')
265
+ }
266
+ end
267
+
268
+ # WsContact
269
+ def parse_contact(node)
270
+ {
271
+ :first_name => xml_to_str(node, './firstName/text()'),
272
+ :middle_initials => xml_to_str(node, './middleInitials/text()'),
273
+ :last_name => xml_to_str(node, './lastName/text()'),
274
+ :cell_phone => xml_to_str(node, './cellPhone/text()'),
275
+ :work_phone => xml_to_str(node, './workPhone/text()'),
276
+ :home_phone => xml_to_str(node, './homePhone/text()'),
277
+ :email => xml_to_str(node, './email/text()'),
278
+ :owner_id => xml_to_str(node, './ownerId/text()')
279
+ }
280
+ end
281
+
282
+ # WsOpportunity
283
+ def parse_opportunity(node)
284
+ {
285
+ #:entity_id => xml_to_str(node, './entityId/text()'),
286
+ #:entity_type => xml_to_str(node, './entityType/text()'),
287
+ :account_id => xml_to_int(node, './accountId/text()'),
288
+ :account_name => xml_to_str(node, './accountName/text()'),
289
+ :opportunity_id => xml_to_int(node, './opportunityId/text()'),
290
+ :name => xml_to_str(node, './name/text()'),
291
+ :deal_value => xml_to_str(node, './dealValue/text()'),
292
+ :confidence => xml_to_str(node, './confidence/text()'),
293
+ :opportunity_status => xml_to_str(node, './opportunityStatus/status/text()'),
294
+ :selling_process => xml_to_str(node, './sellingProcess/sellingProcess/text()'),
295
+ :selling_process_id => xml_to_int(node, './sellingProcess/sellingProcessId/text()'),
296
+ :primary_owner_id => xml_to_int(node, './primaryOwnerId/text()'),
297
+ :current_phase_name => xml_to_str(node, './currentPhaseName/text()')
298
+ }
299
+ end
300
+
301
+ end
302
+
303
+
@@ -0,0 +1,44 @@
1
+
2
+ require 'test_helper'
3
+
4
+ class LandsliderTest < Test::Unit::TestCase
5
+
6
+ def test_landslider_login
7
+ result = Landslider.login('LOGINTOKEN=' + LS_INSTANCE_NAME)
8
+
9
+ assert_not_nil result
10
+ assert_equal false, result[:error]
11
+ assert_not_nil result[:session_id]
12
+ end
13
+
14
+ def test_landslider_get_api_version
15
+ result = Landslider.login('LOGINTOKEN=' + LS_INSTANCE_NAME)
16
+ result = Landslider.get_api_version(result[:session_id])
17
+
18
+ assert_not_nil result
19
+ assert_operator result[:major_version], :>=, 2
20
+ assert_operator result[:minor_version], :>=, 0
21
+ assert_operator result[:revision_version], :>=, 0
22
+ end
23
+
24
+ def test_get_accounts
25
+ result = Landslider.login('LOGINTOKEN=' + LS_INSTANCE_NAME)
26
+ result = Landslider.get_accounts(result[:session_id])
27
+ assert_equal false, result[:error]
28
+ assert_not_nil result[:accounts]
29
+ assert result[:accounts].all? { |a| !a[:account_name].nil? }, "account name required"
30
+ assert_operator result[:results_returned], :>=, 0
31
+ assert_operator result[:total_results_available], :>=, 0
32
+ assert_not_nil result[:result_msg]
33
+ end
34
+
35
+
36
+ private
37
+
38
+ def login(session_id)
39
+ Landslider.login(session_id)
40
+ end
41
+
42
+
43
+ end
44
+
@@ -0,0 +1,16 @@
1
+ require 'digest/md5'
2
+
3
+ # constants to be set by rails environment config files
4
+ LS_INSTANCE_NAME = 'jaytest'
5
+ LS_API_USERNAME = 'XXXXXXXX@landslide.com'
6
+ LS_API_PASSWORD = 'XXXXXXXX'
7
+ LS_API_KEY = Digest::MD5.hexdigest(LS_API_PASSWORD + LS_INSTANCE_NAME)
8
+ LS_API_NAMESPACE = "http://www.landslide.com/webservices/SoapService"
9
+ LS_API_ENDPOINT = {
10
+ :uri => "https://#{LS_INSTANCE_NAME}.api.landslide.com/webservices/SoapService",
11
+ :version => 2
12
+ }
13
+
14
+ require 'test/unit'
15
+ require 'landslider'
16
+
metadata ADDED
@@ -0,0 +1,67 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: landslider
3
+ version: !ruby/object:Gem::Version
4
+ prerelease:
5
+ version: 0.1.0
6
+ platform: ruby
7
+ authors:
8
+ - Jay Prall
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+
13
+ date: 2011-04-22 00:00:00 -04:00
14
+ default_executable:
15
+ dependencies: []
16
+
17
+ description: "Ruby interface to Landslide SOAP API. "
18
+ email: jayprall@gmail.com
19
+ executables: []
20
+
21
+ extensions: []
22
+
23
+ extra_rdoc_files:
24
+ - LICENSE
25
+ - README.rdoc
26
+ files:
27
+ - CHANGELOG
28
+ - LICENSE
29
+ - README.rdoc
30
+ - Rakefile
31
+ - VERSION.yml
32
+ - init.rb
33
+ - landslider.gemspec
34
+ - lib/landslider.rb
35
+ - test/landslider_test.rb
36
+ - test/test_helper.rb
37
+ has_rdoc: true
38
+ homepage: https://github.com/j4y/landslider
39
+ licenses: []
40
+
41
+ post_install_message:
42
+ rdoc_options: []
43
+
44
+ require_paths:
45
+ - lib
46
+ required_ruby_version: !ruby/object:Gem::Requirement
47
+ none: false
48
+ requirements:
49
+ - - ">="
50
+ - !ruby/object:Gem::Version
51
+ version: "0"
52
+ required_rubygems_version: !ruby/object:Gem::Requirement
53
+ none: false
54
+ requirements:
55
+ - - ">="
56
+ - !ruby/object:Gem::Version
57
+ version: "0"
58
+ requirements: []
59
+
60
+ rubyforge_project:
61
+ rubygems_version: 1.6.2
62
+ signing_key:
63
+ specification_version: 3
64
+ summary: Ruby interface to Landslide SOAP API
65
+ test_files:
66
+ - test/landslider_test.rb
67
+ - test/test_helper.rb