connectwise_sdk 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +19 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +88 -0
- data/Rakefile +1 -0
- data/connectwise_sdk.gemspec +26 -0
- data/lib/connectwise/company.rb +5 -0
- data/lib/connectwise/connection.rb +70 -0
- data/lib/connectwise/connectwise.rb +194 -0
- data/lib/connectwise/contact.rb +56 -0
- data/lib/connectwise/extensions.rb +10 -0
- data/lib/connectwise/member.rb +17 -0
- data/lib/connectwise/model.rb +45 -0
- data/lib/connectwise/opportunity.rb +5 -0
- data/lib/connectwise/ticket.rb +5 -0
- data/lib/connectwise/version.rb +3 -0
- data/lib/connectwise_sdk.rb +13 -0
- data/spec/credentials.yml.sample +5 -0
- data/spec/lib/connectwise/company_spec.rb +35 -0
- data/spec/lib/connectwise/connection_spec.rb +27 -0
- data/spec/lib/connectwise/contact_spec.rb +39 -0
- data/spec/lib/connectwise/member_spec.rb +14 -0
- data/spec/lib/connectwise/opportunity_spec.rb +17 -0
- data/spec/lib/connectwise/ticket_spec.rb +16 -0
- data/spec/lib/connectwise_sdk_spec.rb +5 -0
- data/spec/spec_helper.rb +7 -0
- metadata +135 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: f97ff9137957a443af0184ff9f5be0864b3abd0f
|
4
|
+
data.tar.gz: 7348114bd346b8b314659b5006c3cd9117c61e73
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: a4c762a2424ccfd108bfcd55ccfb09be095ad2b9ec6abdb384711c36062dc87a045dc74949bbc3a6d67dcb9c406d97100b11000a8314febdae797702ad239a12
|
7
|
+
data.tar.gz: e94651ab74d558d127b9de456fe3bf794bf4c1d3b32078008f9f291f7e6371c6dd2e40e0e74504df08a9e3fb045d8faa689d011bfbe4353563b97c0dcbc3bbc2
|
data/.gitignore
ADDED
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2014 Emery A. Miller
|
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,88 @@
|
|
1
|
+
# ConnectwiseSdk
|
2
|
+
|
3
|
+
An SDK allowing integration into Connectwise
|
4
|
+
|
5
|
+
## Installation
|
6
|
+
|
7
|
+
Add this line to your application's Gemfile:
|
8
|
+
|
9
|
+
gem 'connectwise_sdk'
|
10
|
+
|
11
|
+
And then execute:
|
12
|
+
|
13
|
+
$ bundle
|
14
|
+
|
15
|
+
Or install it yourself as:
|
16
|
+
|
17
|
+
$ gem install connectwise_sdk
|
18
|
+
|
19
|
+
## Usage
|
20
|
+
|
21
|
+
```ruby
|
22
|
+
conn = Connectwise::Connection.new host: 'host.domain.com', company_name: 'company', integrator_login_id: 'username', integrator_password: 'password'
|
23
|
+
contact = Connectwise::Contact.new(conn, first_name: 'Malcom', last_name: 'Reynolds', email: 'captain@serenity.com')
|
24
|
+
contact.save # => creates a new contact and updates itself with the fields set by connectwise
|
25
|
+
contact.id # => 432
|
26
|
+
|
27
|
+
# Retrieve a list of members
|
28
|
+
member = Connectwise::Member.where(conn, email_address: 'captain@serenity.com')
|
29
|
+
```
|
30
|
+
|
31
|
+
### Using the low level connection object directly
|
32
|
+
|
33
|
+
In the event that a certain api you need to access isn't fully supported by the sdk yet, or if you simply want more direct access to the api, you can use the lower level call method on the connection object.
|
34
|
+
|
35
|
+
```ruby
|
36
|
+
conn = Connectwise::Connection.new host: 'host.domain.com', company_name: 'company', integrator_login_id: 'username', integrator_password: 'password'
|
37
|
+
conn.call :contact, :find_contacts, conditions: 'EmailAddress like "test@test.com"'
|
38
|
+
```
|
39
|
+
|
40
|
+
- The first parameter is the api you wish to use. In this case the `contactApi`.
|
41
|
+
- The second parameter is the specific api call you wish to use (from the Connectwise Api documentation)
|
42
|
+
- The third paramater is the data you wish to send the api
|
43
|
+
|
44
|
+
The connection object will add the credential fields to your api call automatically, and convert the data hash into a SOAP request. Note that because there is some inconsistency in the naming / case used by the api, all data fields must be passed as specified by the api and not in standard Ruby snake case. For example:
|
45
|
+
|
46
|
+
```ruby
|
47
|
+
conn.call :contact, :add_or_update_contact, {contact: {
|
48
|
+
FirstName: contact.first_name,
|
49
|
+
LastName: contact.last_name,
|
50
|
+
Email: contact.email,
|
51
|
+
Phone: contact.phone,
|
52
|
+
}}
|
53
|
+
```
|
54
|
+
|
55
|
+
Note how `contact` is lower case, while `FirstName` and the other fields are camel case with a leading capital. The Connectwise api requires that contact be lower case and the others camel case.
|
56
|
+
|
57
|
+
## Current progress and TODOs
|
58
|
+
|
59
|
+
Currently only the `Member.where` method, and the low level `Connection.call` method are working.
|
60
|
+
|
61
|
+
1. Complete Contact class
|
62
|
+
2. Company class
|
63
|
+
3. Opportunity class
|
64
|
+
4. Ticket class
|
65
|
+
5. Remaining api
|
66
|
+
|
67
|
+
## Contributing
|
68
|
+
|
69
|
+
1. Fork it
|
70
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
71
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
72
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
73
|
+
5. Create new Pull Request
|
74
|
+
|
75
|
+
### Adding support for other Connectwise Classes
|
76
|
+
|
77
|
+
There is a `Connectwise::Model` module that incorporates the core of what it is to be
|
78
|
+
connectwise model. Every Model should include this class.
|
79
|
+
|
80
|
+
Then it's a matter of figuring out the following for each class:
|
81
|
+
- What fields are returned from a find call
|
82
|
+
- What fields are returned from a create call (sometimes this differs from the find call)
|
83
|
+
- Adding these fields to the attr_accessible list to define what parameters are valid
|
84
|
+
- Handling the basic CRUD operations by using the connection object
|
85
|
+
|
86
|
+
Some issues that need to be resolved:
|
87
|
+
- How best to handle nested data structures (A contact has an email address list, a phone number list, etc.)
|
88
|
+
- How best to handle nested objects (A company has a contact)
|
data/Rakefile
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require "bundler/gem_tasks"
|
@@ -0,0 +1,26 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'connectwise/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "connectwise_sdk"
|
8
|
+
spec.version = Connectwise::VERSION
|
9
|
+
spec.authors = ["Emery A. Miller"]
|
10
|
+
spec.email = ["emery.miller@easyofficephone.com"]
|
11
|
+
spec.description = %q{This Gem is an SDK for accessing Connectwise}
|
12
|
+
spec.summary = %q{A Connectwise SDK for Ruby}
|
13
|
+
spec.homepage = "http://github.com/EasyOfficePhone/connectwise-sdk"
|
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_development_dependency "bundler", "~> 1.3"
|
22
|
+
spec.add_development_dependency "rake"
|
23
|
+
spec.add_development_dependency "rspec"
|
24
|
+
|
25
|
+
spec.add_runtime_dependency "savon", "~> 2.0"
|
26
|
+
end
|
@@ -0,0 +1,70 @@
|
|
1
|
+
module Connectwise
|
2
|
+
class ConnectionError < StandardError; end
|
3
|
+
class UnknownHostError < ConnectionError; end
|
4
|
+
class UnknownCompanyError < ConnectionError; end
|
5
|
+
class BadCredentialsError < ConnectionError; end
|
6
|
+
|
7
|
+
class Connection
|
8
|
+
attr_reader :host, :custom_api_mapping
|
9
|
+
attr_accessor :log
|
10
|
+
|
11
|
+
def initialize(host: '', company_name: '', integrator_login_id: '', integrator_password: '', custom_api_mapping: {})
|
12
|
+
@custom_api_mapping = custom_api_mapping
|
13
|
+
@host = host
|
14
|
+
@credentials = {CompanyId: company_name, IntegratorLoginId: integrator_login_id, IntegratorPassword: integrator_password}
|
15
|
+
end
|
16
|
+
|
17
|
+
def call(api, action, message, options: {}, &err_handler)
|
18
|
+
err_handler ||= proc {|err| raise err}
|
19
|
+
|
20
|
+
client = Savon.client(default_options.merge(options).merge(wsdl: wsdl_url(api)))
|
21
|
+
response = client.call action, message: credentials.merge(message)
|
22
|
+
response.body["#{action}_response".to_sym]["#{action}_result".to_sym]
|
23
|
+
rescue Savon::SOAPFault, Savon::UnknownOperationError, SocketError, URI::InvalidURIError => err
|
24
|
+
begin
|
25
|
+
case err.message
|
26
|
+
when /username or password is incorrect/i
|
27
|
+
raise BadCredentialsError.new 'Login or Password are incorrect'
|
28
|
+
when /hostname nor servname/i
|
29
|
+
raise UnknownHostError.new "The host (#{@host}) is not reachable"
|
30
|
+
when /cannot find company.*connectwise config/i
|
31
|
+
raise UnknownCompanyError.new "The company (#{@credentials[:CompanyId]}) cannot be found by Connectwise"
|
32
|
+
else
|
33
|
+
raise ConnectionError.new "An unknown error occurred when contacting Connectwise : \n#{err.message}"
|
34
|
+
end
|
35
|
+
rescue BadCredentialsError, UnknownHostError, UnknownCompanyError, ConnectionError => err
|
36
|
+
err_handler.call(err)
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
private
|
41
|
+
def default_options
|
42
|
+
defaults = { convert_request_keys_to: :none, ssl_verify_mode: :none }
|
43
|
+
defaults = defaults.merge({ log: true, pretty_print_xml: true }) if @log
|
44
|
+
defaults
|
45
|
+
end
|
46
|
+
|
47
|
+
def wsdl_url(api_name)
|
48
|
+
"https://#{host}/v4_6_release/apis/1.5/#{api(api_name)}.asmx?wsdl"
|
49
|
+
end
|
50
|
+
|
51
|
+
def credentials
|
52
|
+
{credentials: @credentials}
|
53
|
+
end
|
54
|
+
|
55
|
+
def api(desired_api)
|
56
|
+
api = api_mapping.fetch(desired_api) {desired_api}.to_s
|
57
|
+
api.extend(Connectwise::Extensions::String)
|
58
|
+
api =~ /Api\z/ ? api : "#{api.camelize}Api"
|
59
|
+
end
|
60
|
+
|
61
|
+
def api_mapping
|
62
|
+
{
|
63
|
+
billing: :generic_billing_transaction,
|
64
|
+
config: :configuration,
|
65
|
+
device: :managed_device,
|
66
|
+
ticket: :service_ticket,
|
67
|
+
}.merge(custom_api_mapping)
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
@@ -0,0 +1,194 @@
|
|
1
|
+
module Connectwise
|
2
|
+
class ConnectionError < StandardError; end
|
3
|
+
class UnknownHostError < ConnectionError; end
|
4
|
+
class UnknownCompanyError < ConnectionError; end
|
5
|
+
class BadCredentialsError < ConnectionError; end
|
6
|
+
|
7
|
+
class Connection
|
8
|
+
attr_reader :host, :custom_api_mapping
|
9
|
+
attr_accessor :log
|
10
|
+
|
11
|
+
def initialize(host: '', company_name: '', integrator_login_id: '', integrator_password: '', custom_api_mapping: {})
|
12
|
+
@custom_api_mapping = custom_api_mapping
|
13
|
+
@host = host
|
14
|
+
@credentials = {CompanyId: company_name, IntegratorLoginId: integrator_login_id, IntegratorPassword: integrator_password}
|
15
|
+
end
|
16
|
+
|
17
|
+
def call(api, action, message, options: {}, &err_handler)
|
18
|
+
err_handler ||= proc {|err| raise err}
|
19
|
+
|
20
|
+
client = Savon.client(default_options.merge(options).merge(wsdl: wsdl_url(api)))
|
21
|
+
response = client.call action, message: credentials.merge(message)
|
22
|
+
response.body["#{action}_response".to_sym]["#{action}_result".to_sym]
|
23
|
+
rescue Savon::SOAPFault, Savon::UnknownOperationError, SocketError, URI::InvalidURIError => err
|
24
|
+
begin
|
25
|
+
case err.message
|
26
|
+
when /username or password is incorrect/i
|
27
|
+
raise BadCredentialsError.new 'Login or Password are incorrect'
|
28
|
+
when /hostname nor servname/i
|
29
|
+
raise UnknownHostError.new "The host (#{@host}) is not reachable"
|
30
|
+
when /cannot find company.*connectwise config/i
|
31
|
+
raise UnknownCompanyError.new "The company (#{@credentials[:CompanyId]}) cannot be found by Connectwise"
|
32
|
+
else
|
33
|
+
raise ConnectionError.new "An unknown error occurred when contacting Connectwise"
|
34
|
+
end
|
35
|
+
rescue BadCredentialsError, UnknownHostError, UnknownCompanyError, ConnectionError => err
|
36
|
+
err_handler.call(err)
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
private
|
41
|
+
def default_options
|
42
|
+
defaults = { convert_request_keys_to: :none, ssl_verify_mode: :none }
|
43
|
+
defaults = defaults.merge({ log: true, pretty_print_xml: true }) if @log
|
44
|
+
defaults
|
45
|
+
end
|
46
|
+
|
47
|
+
def wsdl_url(api_name)
|
48
|
+
"https://#{host}/v4_6_release/apis/1.5/#{api(api_name)}.asmx?wsdl"
|
49
|
+
end
|
50
|
+
|
51
|
+
def credentials
|
52
|
+
{credentials: @credentials}
|
53
|
+
end
|
54
|
+
|
55
|
+
def api(desired_api)
|
56
|
+
api = api_mapping.fetch(desired_api) {desired_api}.to_s
|
57
|
+
api =~ /Api\z/ ? api : "#{camelize(api)}Api"
|
58
|
+
end
|
59
|
+
|
60
|
+
def camelize(term)
|
61
|
+
string = term.to_s.sub(/^[a-z\d]*/) { $&.capitalize }
|
62
|
+
string.gsub(/(?:_|(\/))([a-z\d]*)/i) { "#{$1}#{$2.capitalize}" }
|
63
|
+
end
|
64
|
+
|
65
|
+
def api_mapping
|
66
|
+
{
|
67
|
+
billing: :generic_billing_transaction,
|
68
|
+
config: :configuration,
|
69
|
+
device: :managed_device,
|
70
|
+
ticket: :service_ticket,
|
71
|
+
}.merge(custom_api_mapping)
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
module Model
|
76
|
+
def initialize(**attributes)
|
77
|
+
attributes.each do |attr, value|
|
78
|
+
public_send "#{attr}=", value
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
class Client
|
84
|
+
include Model
|
85
|
+
attr_accessor :first_name, :last_name, :email_address
|
86
|
+
end
|
87
|
+
|
88
|
+
class Company
|
89
|
+
include Model
|
90
|
+
attr_accessor :company_name, :url
|
91
|
+
end
|
92
|
+
|
93
|
+
class Opportunity
|
94
|
+
include Model
|
95
|
+
|
96
|
+
end
|
97
|
+
|
98
|
+
class ServiceTicket
|
99
|
+
include Model
|
100
|
+
|
101
|
+
end
|
102
|
+
|
103
|
+
class BaseAPI
|
104
|
+
attr_reader :connection
|
105
|
+
def initialize(connection)
|
106
|
+
@connection = connection
|
107
|
+
end
|
108
|
+
|
109
|
+
# Members
|
110
|
+
def find_member(user, &err_handler)
|
111
|
+
resp = connection.call 'MemberApi', :find_members, {conditions: "EmailAddress like '#{user.email}'"}, &err_handler
|
112
|
+
(result = resp[:member_find_result]) ? Array(result) : resp
|
113
|
+
end
|
114
|
+
|
115
|
+
# Contact
|
116
|
+
def create_contact(contact, company_id=nil, &err_handler)
|
117
|
+
message = {
|
118
|
+
FirstName: contact.first_name,
|
119
|
+
LastName: contact.last_name,
|
120
|
+
Email: contact.email,
|
121
|
+
Phone: contact.phone,
|
122
|
+
}
|
123
|
+
message = message.merge(CompanyId: company_id) if company_id
|
124
|
+
connection.call 'ContactApi', :add_or_update_contact, {contact: message}, &err_handler
|
125
|
+
end
|
126
|
+
|
127
|
+
def delete_contact(contact, &err_handler)
|
128
|
+
connection.call 'ContactApi', :delete_contact, {id: contact.id}, &err_handler
|
129
|
+
end
|
130
|
+
|
131
|
+
# Companies
|
132
|
+
def find_company(company, &err_handler)
|
133
|
+
resp = connection.call 'CompanyApi', :find_companies, { conditions: "CompanyName like '%#{company.name}%' or CompanyID = '#{company.id}'" }, &err_handler
|
134
|
+
(result = resp[:company_find_result]) ? Array(result) : resp
|
135
|
+
end
|
136
|
+
|
137
|
+
def create_company(company, &err_handler)
|
138
|
+
message = {
|
139
|
+
Id: 0,
|
140
|
+
CompanyName: company.name,
|
141
|
+
CompanyID: company.id.to_s,
|
142
|
+
WebSite: company.url,
|
143
|
+
Status: 'Active',
|
144
|
+
}
|
145
|
+
connection.call 'CompanyApi', :add_company, {company: message}, &err_handler
|
146
|
+
end
|
147
|
+
|
148
|
+
def delete_company(company, &err_handler)
|
149
|
+
connection.call 'CompanyApi', :delete_company, {id: company.id}, &err_handler
|
150
|
+
end
|
151
|
+
|
152
|
+
# Opportunity
|
153
|
+
def create_opportunity(opportunity, company_id, contact_id, &err_handler)
|
154
|
+
message = {
|
155
|
+
Id: 0,
|
156
|
+
OpportunityName: "Opportunity from Easyofficephone - #{opportunity.company.name}",
|
157
|
+
Company: {CompanyID: company_id},
|
158
|
+
Contact: {Id: contact_id},
|
159
|
+
Source: 'Easy Office Phone opportunity',
|
160
|
+
PrimarySalesRep: opportunity.user.full_name,
|
161
|
+
Notes: {
|
162
|
+
Note: {
|
163
|
+
note_text: opportunity.note,
|
164
|
+
}
|
165
|
+
},
|
166
|
+
}
|
167
|
+
|
168
|
+
connection.call 'OpportunityApi', :add_opportunity, {opportunity: message}, &err_handler
|
169
|
+
end
|
170
|
+
|
171
|
+
def delete_opportunity(opportunity, &err_handler)
|
172
|
+
connection.call 'OpportunityApi', :delete_opportunity, {id: opportunity.id}, &err_handler
|
173
|
+
end
|
174
|
+
|
175
|
+
# Ticket
|
176
|
+
#TODO - these find methods are too limited, need to allow proper query
|
177
|
+
def find_ticket(ticket, company)
|
178
|
+
resp = connection.call 'ServiceTicketApi', :find_service_tickets, {conditions: "CompanyName like '%#{company.name}%' or SRServiceRecID = '#{ticket.id}'"}
|
179
|
+
(result = resp[:tickets]) ? Array(result) : resp
|
180
|
+
end
|
181
|
+
|
182
|
+
def create_ticket(ticket, company, &err_handler)
|
183
|
+
resp = connection.call 'ServiceTicketApi', :add_service_ticket_via_company_id, {
|
184
|
+
serviceTicket: {
|
185
|
+
Id: 0,
|
186
|
+
CompanyId: company.id,
|
187
|
+
Summary: ticket.subject,
|
188
|
+
Status: 'N',
|
189
|
+
ProblemDescription: ticket.description,
|
190
|
+
}
|
191
|
+
}, &err_handler
|
192
|
+
end
|
193
|
+
end
|
194
|
+
end
|
@@ -0,0 +1,56 @@
|
|
1
|
+
module Connectwise
|
2
|
+
class Contact
|
3
|
+
include Model
|
4
|
+
#TODO - email or email_address - Member uses email_address, while here it's email - normalize
|
5
|
+
attr_accessor :id, :first_name, :last_name, :company_name, :phone, :email, :type, :relationship, :default_flag, :address_line1, :address_line2, :city, :state, :zip, :country,
|
6
|
+
:portal_security_level, :portal_security_caption, :disable_portal_login, :last_update
|
7
|
+
|
8
|
+
def self.where(connection, **attrs)
|
9
|
+
resp = connection.call :contact, :find_contacts, {conditions: attrs_to_query(attrs)}
|
10
|
+
Array(resp[:contact_find_result]).map {|attrs| p attrs; cw_find_to_model(connection, attrs) }
|
11
|
+
end
|
12
|
+
|
13
|
+
def save
|
14
|
+
#message = message.merge(CompanyId: company_id) if company_id
|
15
|
+
attrs = connection.call 'ContactApi', :add_or_update_contact, {contact: to_cw_h}
|
16
|
+
p attrs
|
17
|
+
self.class.cw_save_to_model(connection, attrs)
|
18
|
+
end
|
19
|
+
|
20
|
+
def destroy
|
21
|
+
|
22
|
+
end
|
23
|
+
|
24
|
+
def persisted?
|
25
|
+
!!@id
|
26
|
+
end
|
27
|
+
|
28
|
+
private
|
29
|
+
def self.cw_find_to_model(conn, attrs)
|
30
|
+
id = attrs.delete(:contact_rec_id)
|
31
|
+
#TODO - make use of company rec id - either run another query to create it, or find a way to defer until company is asked for
|
32
|
+
company_id = attrs.delete(:company_rec_id)
|
33
|
+
self.new(conn, id: id, **attrs)
|
34
|
+
end
|
35
|
+
|
36
|
+
def self.cw_save_to_model(conn, attrs)
|
37
|
+
|
38
|
+
end
|
39
|
+
|
40
|
+
def create_contact(contact, company_id=nil, &err_handler)
|
41
|
+
message = {
|
42
|
+
FirstName: contact.first_name,
|
43
|
+
LastName: contact.last_name,
|
44
|
+
Email: contact.email,
|
45
|
+
Phone: contact.phone,
|
46
|
+
}
|
47
|
+
message = message.merge(CompanyId: company_id) if company_id
|
48
|
+
connection.call 'ContactApi', :add_or_update_contact, {contact: message}, &err_handler
|
49
|
+
end
|
50
|
+
|
51
|
+
def delete_contact(contact, &err_handler)
|
52
|
+
connection.call 'ContactApi', :delete_contact, {id: contact.id}, &err_handler
|
53
|
+
end
|
54
|
+
|
55
|
+
end
|
56
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
module Connectwise
|
2
|
+
class Member
|
3
|
+
include Model
|
4
|
+
attr_accessor :email_address, :first_name, :last_name, :id
|
5
|
+
|
6
|
+
def self.where(connection, **attrs)
|
7
|
+
resp = connection.call :member, :find_members, {conditions: attrs_to_query(attrs)}
|
8
|
+
Array(resp[:member_find_result]).map {|attrs| cw_to_model(connection, attrs) }
|
9
|
+
end
|
10
|
+
|
11
|
+
private
|
12
|
+
def self.cw_to_model(conn, attrs)
|
13
|
+
id = attrs.delete(:member_id)
|
14
|
+
self.new(conn, id: id, **attrs)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
module Connectwise
|
2
|
+
module Model
|
3
|
+
module ClassMethods
|
4
|
+
def attrs_to_query(attrs)
|
5
|
+
attrs.map do |k,v|
|
6
|
+
str = k.to_s
|
7
|
+
str.extend(Connectwise::Extensions::String)
|
8
|
+
"#{str.camelize} like '#{v}'"
|
9
|
+
end.join(' and ')
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
def self.included(klass)
|
14
|
+
klass.extend ClassMethods
|
15
|
+
end
|
16
|
+
|
17
|
+
def initialize(connection, **attributes)
|
18
|
+
@connection = connection
|
19
|
+
attributes.each do |attr, value|
|
20
|
+
public_send "#{attr}=", value
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def persisted?
|
25
|
+
false
|
26
|
+
end
|
27
|
+
|
28
|
+
def to_h
|
29
|
+
defined_attributes.each_with_object({}) {|name, memo| memo[name] = public_send(name)}
|
30
|
+
end
|
31
|
+
|
32
|
+
def to_cw_h
|
33
|
+
defined_attributes.each_with_object({}) {|name, memo| key = name.to_s.extend(Extensions::String); memo[key.camelize] = public_send(name)}
|
34
|
+
end
|
35
|
+
|
36
|
+
def defined_attributes
|
37
|
+
instance_vars = instance_variables.map {|name| name.to_s.gsub(/@/, '').to_sym}
|
38
|
+
public_methods.select{|method| instance_vars.include?(method) }
|
39
|
+
end
|
40
|
+
protected
|
41
|
+
def connection
|
42
|
+
@connection
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
require 'savon'
|
2
|
+
require "connectwise/version"
|
3
|
+
require 'connectwise/extensions'
|
4
|
+
require 'connectwise/connection'
|
5
|
+
require 'connectwise/model'
|
6
|
+
require 'connectwise/member'
|
7
|
+
require 'connectwise/contact'
|
8
|
+
require 'connectwise/company'
|
9
|
+
require 'connectwise/opportunity'
|
10
|
+
require 'connectwise/ticket'
|
11
|
+
|
12
|
+
module Connectwise
|
13
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Connectwise::Company do
|
4
|
+
it 'creates a company' do
|
5
|
+
expect(subject.create_company(company)[:id]).not_to be_empty
|
6
|
+
end
|
7
|
+
|
8
|
+
it 'fails to create a company' do
|
9
|
+
company.id = nil
|
10
|
+
expect {subject.create_company(company)}.to raise_error Connectwise::ConnectionError
|
11
|
+
end
|
12
|
+
|
13
|
+
it 'fails to create company and throws error' do
|
14
|
+
company.id = nil
|
15
|
+
expect {subject.create_company(company) do |err|
|
16
|
+
fail ArgumentError
|
17
|
+
end}.to raise_error ArgumentError
|
18
|
+
end
|
19
|
+
|
20
|
+
it 'deletes a company' do
|
21
|
+
new_company = subject.create_company(company)
|
22
|
+
expect(subject.delete_company(OpenStruct.new(id: new_company[:id]))).to be_nil
|
23
|
+
end
|
24
|
+
|
25
|
+
it 'finds a company' do
|
26
|
+
resp = subject.find_company(OpenStruct.new name: 'Blue Sun', id: '6417')
|
27
|
+
expect(resp).not_to be_empty
|
28
|
+
end
|
29
|
+
|
30
|
+
it 'creates a company and a contact and connects them' do
|
31
|
+
new_company = subject.create_company(company)
|
32
|
+
resp = subject.create_contact(contact, new_company[:company_id])
|
33
|
+
expect(resp[:company_id]).to eql new_company[:company_id]
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Connectwise::Connection do
|
4
|
+
let(:credentials) { connectwise_credentials }
|
5
|
+
subject { Connectwise::Connection.new(credentials) }
|
6
|
+
|
7
|
+
describe 'Failures' do
|
8
|
+
it 'raises an UnknownHostError' do
|
9
|
+
subject = Connectwise::Connection.new(credentials.merge(host: 'badhost.connectwisebad.com'))
|
10
|
+
expect {subject.call(:contact, :find_contacts, {})}.to raise_error Connectwise::UnknownHostError
|
11
|
+
end
|
12
|
+
|
13
|
+
it 'raises an UnknownCompanyError' do
|
14
|
+
subject = Connectwise::Connection.new(credentials.merge(company_name: 'badcompanyname'))
|
15
|
+
expect {subject.call(:contact, :find_contacts, {})}.to raise_error Connectwise::UnknownCompanyError
|
16
|
+
end
|
17
|
+
|
18
|
+
it 'raises a BadCredentialsError' do
|
19
|
+
subject = Connectwise::Connection.new(credentials.merge(integrator_login_id: 'badloginname'))
|
20
|
+
expect {subject.call(:contact, :find_contacts, {})}.to raise_error Connectwise::BadCredentialsError
|
21
|
+
end
|
22
|
+
|
23
|
+
it 'raises a ConnectionError' do
|
24
|
+
expect {subject.call(:contact, :delete_contact, {})}.to raise_error Connectwise::ConnectionError
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Connectwise::Contact do
|
4
|
+
let(:credentials) { connectwise_credentials }
|
5
|
+
let(:conn) { Connectwise::Connection.new(credentials) }
|
6
|
+
let(:contact) { OpenStruct.new first_name: 'Malcom', last_name: 'Reynolds' }
|
7
|
+
subject {Connectwise::Contact.new(conn, contact.to_h)}
|
8
|
+
|
9
|
+
it 'creates a contact' do
|
10
|
+
contact_id = contact.id
|
11
|
+
new_contact = subject.save
|
12
|
+
expect(new_contact.persisted?).to be_true
|
13
|
+
expect(new_contact.id).not_to be_empty
|
14
|
+
expect(contact_id).not_to eq new_contact.id
|
15
|
+
end
|
16
|
+
|
17
|
+
it 'finds a contact' do
|
18
|
+
new_contact = subject.save
|
19
|
+
new_contact = contact
|
20
|
+
found_contacts = Connectwise::Contact.where(conn, first_name: new_contact.first_name, last_name: new_contact.last_name)
|
21
|
+
expect(found_contacts).not_to be_empty
|
22
|
+
end
|
23
|
+
|
24
|
+
it 'finds no contact' do
|
25
|
+
new_contact = subject.save
|
26
|
+
found_contacts = Connectwise::Contact.where(conn, first_name: 'gobledy', last_name: 'gook')
|
27
|
+
expect(found_contacts).to be_empty
|
28
|
+
end
|
29
|
+
|
30
|
+
it 'deletes a contact' do
|
31
|
+
new_contact = subject.save
|
32
|
+
found_contacts = Connectwise::Contact.where(conn, first_name: new_contact.first_name, last_name: new_contact.last_name)
|
33
|
+
current_count = found_contacts.count
|
34
|
+
deleted_contact = found_contacts.first.destroy
|
35
|
+
expect(deleted_contact).not_to be_nil
|
36
|
+
found_contacts = Connectwise::Contact.where(conn, first_name: new_contact.first_name, last_name: new_contact.last_name)
|
37
|
+
expect(current_count - found_contacts.count).to eq 1
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Connectwise::Member do
|
4
|
+
let(:credentials) { connectwise_credentials }
|
5
|
+
let(:conn) { Connectwise::Connection.new(credentials) }
|
6
|
+
subject {Connectwise::Member}
|
7
|
+
|
8
|
+
# Note this test relies on the default connectwise test setup where 'test@test.com' is every member's email
|
9
|
+
it 'finds a member' do
|
10
|
+
response = subject.where(conn, email_address: 'test@test.com')
|
11
|
+
expect(response.first.class).to eq Connectwise::Member
|
12
|
+
expect(response.first.email_address).to eq 'test@test.com'
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Connectwise::Opportunity do
|
4
|
+
it 'creates an opportunity' do
|
5
|
+
new_company = subject.create_company(company)
|
6
|
+
new_contact = subject.create_contact(contact, new_company[:company_id])
|
7
|
+
resp = subject.create_opportunity(lead, new_company[:company_id], new_contact[:id])
|
8
|
+
expect(resp[:id]).not_to be_empty
|
9
|
+
end
|
10
|
+
|
11
|
+
it 'deletes an opportunity' do
|
12
|
+
new_company = subject.create_company(company)
|
13
|
+
new_contact = subject.create_contact(contact, new_company[:company_id])
|
14
|
+
new_opp = subject.create_opportunity(lead, new_company[:company_id], new_contact[:id])
|
15
|
+
expect(subject.delete_opportunity(OpenStruct.new(id: new_opp[:id]))).to be_nil
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Connectwise::Ticket do
|
4
|
+
it 'creates a ticket' do
|
5
|
+
new_company = subject.create_company(company)
|
6
|
+
resp = subject.create_ticket(OpenStruct.new(subject: 'help', description: 'abcd go boom'), OpenStruct.new(id: new_company[:company_id]))
|
7
|
+
expect(resp[:ticket_number]).not_to be_nil
|
8
|
+
end
|
9
|
+
|
10
|
+
it 'finds a service ticket' do
|
11
|
+
new_company = subject.create_company(company)
|
12
|
+
new_ticket = subject.create_ticket(OpenStruct.new(subject: 'help', description: 'abcd go boom'), OpenStruct.new(id: new_company[:company_id]))
|
13
|
+
resp = subject.find_ticket(OpenStruct.new(id: new_ticket[:ticket_number]), OpenStruct.new(name: 'Gobledygook ragnok ramastrodon'))
|
14
|
+
expect(resp).not_to be_empty
|
15
|
+
end
|
16
|
+
end
|
data/spec/spec_helper.rb
ADDED
metadata
ADDED
@@ -0,0 +1,135 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: connectwise_sdk
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Emery A. Miller
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2014-09-17 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: bundler
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '1.3'
|
20
|
+
type: :development
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '1.3'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: rake
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ">="
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: rspec
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ">="
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ">="
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: savon
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - "~>"
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '2.0'
|
62
|
+
type: :runtime
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - "~>"
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '2.0'
|
69
|
+
description: This Gem is an SDK for accessing Connectwise
|
70
|
+
email:
|
71
|
+
- emery.miller@easyofficephone.com
|
72
|
+
executables: []
|
73
|
+
extensions: []
|
74
|
+
extra_rdoc_files: []
|
75
|
+
files:
|
76
|
+
- ".gitignore"
|
77
|
+
- Gemfile
|
78
|
+
- LICENSE.txt
|
79
|
+
- README.md
|
80
|
+
- Rakefile
|
81
|
+
- connectwise_sdk.gemspec
|
82
|
+
- lib/connectwise/company.rb
|
83
|
+
- lib/connectwise/connection.rb
|
84
|
+
- lib/connectwise/connectwise.rb
|
85
|
+
- lib/connectwise/contact.rb
|
86
|
+
- lib/connectwise/extensions.rb
|
87
|
+
- lib/connectwise/member.rb
|
88
|
+
- lib/connectwise/model.rb
|
89
|
+
- lib/connectwise/opportunity.rb
|
90
|
+
- lib/connectwise/ticket.rb
|
91
|
+
- lib/connectwise/version.rb
|
92
|
+
- lib/connectwise_sdk.rb
|
93
|
+
- spec/credentials.yml.sample
|
94
|
+
- spec/lib/connectwise/company_spec.rb
|
95
|
+
- spec/lib/connectwise/connection_spec.rb
|
96
|
+
- spec/lib/connectwise/contact_spec.rb
|
97
|
+
- spec/lib/connectwise/member_spec.rb
|
98
|
+
- spec/lib/connectwise/opportunity_spec.rb
|
99
|
+
- spec/lib/connectwise/ticket_spec.rb
|
100
|
+
- spec/lib/connectwise_sdk_spec.rb
|
101
|
+
- spec/spec_helper.rb
|
102
|
+
homepage: http://github.com/EasyOfficePhone/connectwise-sdk
|
103
|
+
licenses:
|
104
|
+
- MIT
|
105
|
+
metadata: {}
|
106
|
+
post_install_message:
|
107
|
+
rdoc_options: []
|
108
|
+
require_paths:
|
109
|
+
- lib
|
110
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
111
|
+
requirements:
|
112
|
+
- - ">="
|
113
|
+
- !ruby/object:Gem::Version
|
114
|
+
version: '0'
|
115
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
116
|
+
requirements:
|
117
|
+
- - ">="
|
118
|
+
- !ruby/object:Gem::Version
|
119
|
+
version: '0'
|
120
|
+
requirements: []
|
121
|
+
rubyforge_project:
|
122
|
+
rubygems_version: 2.2.2
|
123
|
+
signing_key:
|
124
|
+
specification_version: 4
|
125
|
+
summary: A Connectwise SDK for Ruby
|
126
|
+
test_files:
|
127
|
+
- spec/credentials.yml.sample
|
128
|
+
- spec/lib/connectwise/company_spec.rb
|
129
|
+
- spec/lib/connectwise/connection_spec.rb
|
130
|
+
- spec/lib/connectwise/contact_spec.rb
|
131
|
+
- spec/lib/connectwise/member_spec.rb
|
132
|
+
- spec/lib/connectwise/opportunity_spec.rb
|
133
|
+
- spec/lib/connectwise/ticket_spec.rb
|
134
|
+
- spec/lib/connectwise_sdk_spec.rb
|
135
|
+
- spec/spec_helper.rb
|