agilecrm-wrapper 1.0.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.
- checksums.yaml +7 -0
- data/.gitignore +20 -0
- data/.rspec +2 -0
- data/.rubocop.yml +9 -0
- data/Gemfile +4 -0
- data/Gemfile.lock +89 -0
- data/LICENSE +21 -0
- data/README.md +119 -0
- data/Rakefile +10 -0
- data/agilecrm.gemspec +35 -0
- data/lib/agilecrm-wrapper.rb +47 -0
- data/lib/agilecrm-wrapper/configuration.rb +11 -0
- data/lib/agilecrm-wrapper/contact.rb +119 -0
- data/lib/agilecrm-wrapper/error.rb +75 -0
- data/lib/agilecrm-wrapper/note.rb +31 -0
- data/lib/agilecrm-wrapper/response/raise_error.rb +21 -0
- data/lib/agilecrm-wrapper/version.rb +3 -0
- data/spec/agilecrm-wrapper/agilecrm_wrapper_spec.rb +45 -0
- data/spec/agilecrm-wrapper/contact_spec.rb +108 -0
- data/spec/agilecrm-wrapper/note_spec.rb +11 -0
- data/spec/fixtures/contacts/create_contact.json +45 -0
- data/spec/fixtures/contacts/get_contact.json +45 -0
- data/spec/fixtures/contacts/get_contact_notes.json +52 -0
- data/spec/fixtures/contacts/list_contacts.json +100 -0
- data/spec/fixtures/contacts/search_by_email.json +45 -0
- data/spec/fixtures/contacts/search_by_email_no_results.json +1 -0
- data/spec/fixtures/contacts/updated_contact.json +45 -0
- data/spec/fixtures/notes/create_note.json +52 -0
- data/spec/fixtures/notes/create_without_contact.json +8 -0
- data/spec/spec_helper.rb +20 -0
- data/spec/support/fake_agilecrm.rb +61 -0
- metadata +270 -0
@@ -0,0 +1,75 @@
|
|
1
|
+
module AgileCRMWrapper
|
2
|
+
class Error < StandardError
|
3
|
+
attr_reader :response
|
4
|
+
|
5
|
+
class << self
|
6
|
+
def from_response(response, message = '')
|
7
|
+
new(response, message)
|
8
|
+
end
|
9
|
+
|
10
|
+
def errors
|
11
|
+
@errors ||= {
|
12
|
+
400 => AgileCRMWrapper::BadRequest,
|
13
|
+
401 => AgileCRMWrapper::Unauthorized,
|
14
|
+
404 => AgileCRMWrapper::NotFound,
|
15
|
+
405 => AgileCRMWrapper::MethodNotAllowed,
|
16
|
+
415 => AgileCRMWrapper::MediaTypeMismatch,
|
17
|
+
500 => AgileCRMWrapper::InternalServerError
|
18
|
+
}
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def initialize(response, message = '')
|
23
|
+
super(message)
|
24
|
+
@response = response
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
# Raised when Twitter returns a 4xx HTTP status code
|
29
|
+
class ClientError < Error; end
|
30
|
+
|
31
|
+
# Raised when AgileCRMWrapper returns a 400 HTTP status code
|
32
|
+
class BadRequest < ClientError
|
33
|
+
def initialize(response, message = 'The request was formatted incorrectly')
|
34
|
+
super(response, message)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
# Raised when AgileCRMWrapper returns a 401 HTTP status code
|
39
|
+
class Unauthorized < ClientError
|
40
|
+
def initialize(response, message = 'Invalid API Key')
|
41
|
+
super(response, message)
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
# Raised when AgileCRMWrapper returns a 404 HTTP status code
|
46
|
+
class NotFound < ClientError
|
47
|
+
def initialize(response, message = 'Resource not found')
|
48
|
+
super(response, message)
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
# Raised when AgileCRMWrapper returns a 405 HTTP status code
|
53
|
+
class MethodNotAllowed < ClientError
|
54
|
+
def initialize(response, message = 'Invalid method type')
|
55
|
+
super(response, message)
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
# Raised when AgileCRMWrapper returns a 415 HTTP status code
|
60
|
+
class MediaTypeMismatch < ClientError
|
61
|
+
def initialize(response, message = 'Unsupported Media type')
|
62
|
+
super(response, message)
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
# Raised when AgileCRMWrapper returns a 5xx HTTP status code
|
67
|
+
class ServerError < Error; end
|
68
|
+
|
69
|
+
# Raised when AgileCRMWrapper returns a 500 HTTP status code
|
70
|
+
class InternalServerError < ServerError
|
71
|
+
def initialize(response, message = 'Server error')
|
72
|
+
super(response, message)
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
require 'agilecrm-wrapper/error'
|
2
|
+
require 'hashie'
|
3
|
+
|
4
|
+
module AgileCRMWrapper
|
5
|
+
class Note < Hashie::Mash
|
6
|
+
class << self
|
7
|
+
def create(*contacts, subject: '', description: '')
|
8
|
+
contacts = contacts.flatten.uniq.map(&:to_s)
|
9
|
+
payload = {
|
10
|
+
'subject' => subject,
|
11
|
+
'description' => description,
|
12
|
+
'contact_ids' => contacts
|
13
|
+
}
|
14
|
+
response = AgileCRMWrapper.connection.post('notes', payload)
|
15
|
+
new(response.body)
|
16
|
+
end
|
17
|
+
|
18
|
+
def add_by_email(email: '', subject: '', description: '')
|
19
|
+
payload = {
|
20
|
+
'subject' => subject,
|
21
|
+
'description' => description
|
22
|
+
}
|
23
|
+
query = "email=#{email}¬e=#{payload.to_json}"
|
24
|
+
AgileCRMWrapper.connection.post(
|
25
|
+
'contacts/email/note/add', query,
|
26
|
+
'content-type' => 'application/x-www-form-urlencoded'
|
27
|
+
)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
require 'faraday'
|
2
|
+
require 'agilecrm-wrapper/error'
|
3
|
+
|
4
|
+
module AgileCRMWrapper
|
5
|
+
module Response
|
6
|
+
class RaiseError < Faraday::Response::Middleware
|
7
|
+
private
|
8
|
+
|
9
|
+
def on_complete(response)
|
10
|
+
status_code = response.status.to_i
|
11
|
+
klass = AgileCRMWrapper::Error.errors[status_code]
|
12
|
+
return unless klass
|
13
|
+
fail(klass.from_response(response))
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
Faraday::Response.register_middleware(
|
20
|
+
agilecrm_error: AgileCRMWrapper::Response::RaiseError
|
21
|
+
)
|
@@ -0,0 +1,45 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe AgileCRMWrapper do
|
4
|
+
let(:domain) { 'mydomain' }
|
5
|
+
let(:api_key) { 'xxx' }
|
6
|
+
let(:email) { 'email@example.com' }
|
7
|
+
|
8
|
+
describe 'configuration' do
|
9
|
+
before(:each) do
|
10
|
+
AgileCRMWrapper.configure do |config|
|
11
|
+
config.domain = domain
|
12
|
+
config.api_key = api_key
|
13
|
+
config.email = email
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
after :each do
|
18
|
+
AgileCRMWrapper.reset
|
19
|
+
end
|
20
|
+
|
21
|
+
describe '.configure' do
|
22
|
+
it 'has a custom configuration' do
|
23
|
+
expect(AgileCRMWrapper.configuration.domain).to match domain
|
24
|
+
expect(AgileCRMWrapper.configuration.api_key).to match api_key
|
25
|
+
expect(AgileCRMWrapper.configuration.email).to match email
|
26
|
+
end
|
27
|
+
|
28
|
+
its(:endpoint) { should eq 'https://mydomain.agilecrm.com/dev/api' }
|
29
|
+
end
|
30
|
+
|
31
|
+
describe '.reset' do
|
32
|
+
before(:each) do
|
33
|
+
AgileCRMWrapper.configure do |config|
|
34
|
+
config.email = email
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
it 'resets the configuration' do
|
39
|
+
AgileCRMWrapper.reset
|
40
|
+
config = AgileCRMWrapper.configuration
|
41
|
+
expect(config.email).to eq ''
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
@@ -0,0 +1,108 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe AgileCRMWrapper::Contact do
|
4
|
+
let(:contact) { AgileCRMWrapper::Contact.find(123) }
|
5
|
+
|
6
|
+
describe '.all' do
|
7
|
+
subject { AgileCRMWrapper::Contact.all }
|
8
|
+
|
9
|
+
it 'should return an array of Contacts' do
|
10
|
+
expect(subject.map(&:class).uniq).to eq([AgileCRMWrapper::Contact])
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
describe '.find' do
|
15
|
+
let(:id) { 123 }
|
16
|
+
subject { AgileCRMWrapper::Contact.find(id) }
|
17
|
+
|
18
|
+
context 'given an existing contact ID' do
|
19
|
+
it { should be_kind_of(AgileCRMWrapper::Contact) }
|
20
|
+
|
21
|
+
its(:id) { should eq id }
|
22
|
+
end
|
23
|
+
|
24
|
+
context 'given an unknown contact ID' do
|
25
|
+
let(:id) { 0 }
|
26
|
+
it { expect { is_expected.to raise_error(AgileCRMWrapper::NotFound) } }
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
describe '.delete' do
|
31
|
+
context 'given a single ID' do
|
32
|
+
subject { AgileCRMWrapper::Contact.delete(123) }
|
33
|
+
|
34
|
+
its(:status) { should eq 204 }
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
describe '.search_by_email' do
|
39
|
+
let(:email) { 'anitadrink@example.com' }
|
40
|
+
subject { AgileCRMWrapper::Contact.search_by_email(email) }
|
41
|
+
|
42
|
+
context 'given an existing email' do
|
43
|
+
it 'should return a contact with the corresponding email' do
|
44
|
+
expect(subject.get_property('email')).to eq email
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
context 'given an non-existing email' do
|
49
|
+
let(:email) { 'idontexist@example.com' }
|
50
|
+
|
51
|
+
it 'should return an empty array' do
|
52
|
+
expect(subject).to eq nil
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
describe '.create' do
|
58
|
+
subject do
|
59
|
+
AgileCRMWrapper::Contact.create(
|
60
|
+
tags: %w(sales, rspec), first_name: 'Anita',
|
61
|
+
last_name: 'Drink', email: 'anitadrink@example.com',
|
62
|
+
custom_field: 'Im a custom field!'
|
63
|
+
)
|
64
|
+
end
|
65
|
+
|
66
|
+
its(:status) { should eq 201 }
|
67
|
+
end
|
68
|
+
|
69
|
+
describe '#notes' do
|
70
|
+
it 'returns the associated notes' do
|
71
|
+
expect(contact.notes.map(&:class).uniq).to eq [AgileCRMWrapper::Note]
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
describe '#update' do
|
76
|
+
|
77
|
+
it 'updates the receiving contact with the supplied key-value pair(s)' do
|
78
|
+
expect do
|
79
|
+
contact.update(first_name: 'Foo!')
|
80
|
+
end.to change{
|
81
|
+
contact.get_property('first_name')
|
82
|
+
}.from('Anita').to('Foo!')
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
describe '#get_property' do
|
87
|
+
let(:contact) { AgileCRMWrapper::Contact.find(123) }
|
88
|
+
|
89
|
+
context 'supplied an existing property name' do
|
90
|
+
it 'returns the value' do
|
91
|
+
expect(contact.get_property('email')).to_not be_nil
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
context 'supplied a non-existing property name' do
|
96
|
+
it 'returns nil' do
|
97
|
+
expect(contact.get_property('nil-propety')).to be_nil
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
describe '#destroy' do
|
103
|
+
let(:contact) { AgileCRMWrapper::Contact.find(123) }
|
104
|
+
subject { contact.destroy }
|
105
|
+
|
106
|
+
its(:status) { should eq 204 }
|
107
|
+
end
|
108
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
{
|
2
|
+
"id": 123,
|
3
|
+
"type": "PERSON",
|
4
|
+
"created_time": 1412704793,
|
5
|
+
"updated_time": 0,
|
6
|
+
"viewed_time": 0,
|
7
|
+
"viewed": {
|
8
|
+
"viewed_time": 0
|
9
|
+
},
|
10
|
+
"star_value": 0,
|
11
|
+
"lead_score": 0,
|
12
|
+
"tags": ["sales", "rspec"],
|
13
|
+
"tagsWithTime": [{
|
14
|
+
"tag": "sales",
|
15
|
+
"createdTime": 1412704793874,
|
16
|
+
"availableCount": 0,
|
17
|
+
"entity_type": "tag"
|
18
|
+
}, {
|
19
|
+
"tag": "rspec",
|
20
|
+
"createdTime": 1412704793874,
|
21
|
+
"availableCount": 0,
|
22
|
+
"entity_type": "tag"
|
23
|
+
}],
|
24
|
+
"properties": [{
|
25
|
+
"type": "SYSTEM",
|
26
|
+
"name": "first_name",
|
27
|
+
"value": "Anita"
|
28
|
+
}, {
|
29
|
+
"type": "SYSTEM",
|
30
|
+
"name": "last_name",
|
31
|
+
"value": "Drink"
|
32
|
+
}, {
|
33
|
+
"type": "SYSTEM",
|
34
|
+
"name": "email",
|
35
|
+
"value": "anitadrink@example.com"
|
36
|
+
}, {
|
37
|
+
"type": "CUSTOM",
|
38
|
+
"name": "custom_field",
|
39
|
+
"value": "Im a custom field!"
|
40
|
+
}],
|
41
|
+
"campaignStatus": [],
|
42
|
+
"entity_type": "contact_entity",
|
43
|
+
"unsubscribeStatus": [],
|
44
|
+
"emailBounceStatus": []
|
45
|
+
}
|
@@ -0,0 +1,45 @@
|
|
1
|
+
{
|
2
|
+
"id": 123,
|
3
|
+
"type": "PERSON",
|
4
|
+
"created_time": 1412704793,
|
5
|
+
"updated_time": 0,
|
6
|
+
"viewed_time": 0,
|
7
|
+
"viewed": {
|
8
|
+
"viewed_time": 0
|
9
|
+
},
|
10
|
+
"star_value": 0,
|
11
|
+
"lead_score": 0,
|
12
|
+
"tags": ["sales", "rspec"],
|
13
|
+
"tagsWithTime": [{
|
14
|
+
"tag": "sales",
|
15
|
+
"createdTime": 1412704793874,
|
16
|
+
"availableCount": 0,
|
17
|
+
"entity_type": "tag"
|
18
|
+
}, {
|
19
|
+
"tag": "rspec",
|
20
|
+
"createdTime": 1412704793874,
|
21
|
+
"availableCount": 0,
|
22
|
+
"entity_type": "tag"
|
23
|
+
}],
|
24
|
+
"properties": [{
|
25
|
+
"type": "SYSTEM",
|
26
|
+
"name": "first_name",
|
27
|
+
"value": "Anita"
|
28
|
+
}, {
|
29
|
+
"type": "SYSTEM",
|
30
|
+
"name": "last_name",
|
31
|
+
"value": "Drink"
|
32
|
+
}, {
|
33
|
+
"type": "SYSTEM",
|
34
|
+
"name": "email",
|
35
|
+
"value": "anitadrink@example.com"
|
36
|
+
}, {
|
37
|
+
"type": "CUSTOM",
|
38
|
+
"name": "custom_field",
|
39
|
+
"value": "Im a custom field!"
|
40
|
+
}],
|
41
|
+
"campaignStatus": [],
|
42
|
+
"entity_type": "contact_entity",
|
43
|
+
"unsubscribeStatus": [],
|
44
|
+
"emailBounceStatus": []
|
45
|
+
}
|
@@ -0,0 +1,52 @@
|
|
1
|
+
[{
|
2
|
+
"id": 80001,
|
3
|
+
"created_time": 1360561958,
|
4
|
+
"subject": "Note subject",
|
5
|
+
"description": "Note description",
|
6
|
+
"contacts": [{
|
7
|
+
"id": 123,
|
8
|
+
"type": "PERSON",
|
9
|
+
"created_time": 1412704793,
|
10
|
+
"updated_time": 0,
|
11
|
+
"viewed_time": 0,
|
12
|
+
"viewed": {
|
13
|
+
"viewed_time": 0
|
14
|
+
},
|
15
|
+
"star_value": 0,
|
16
|
+
"lead_score": 0,
|
17
|
+
"tags": ["sales", "rspec"],
|
18
|
+
"tagsWithTime": [{
|
19
|
+
"tag": "sales",
|
20
|
+
"createdTime": 1412704793874,
|
21
|
+
"availableCount": 0,
|
22
|
+
"entity_type": "tag"
|
23
|
+
}, {
|
24
|
+
"tag": "rspec",
|
25
|
+
"createdTime": 1412704793874,
|
26
|
+
"availableCount": 0,
|
27
|
+
"entity_type": "tag"
|
28
|
+
}],
|
29
|
+
"properties": [{
|
30
|
+
"type": "SYSTEM",
|
31
|
+
"name": "first_name",
|
32
|
+
"value": "Anita"
|
33
|
+
}, {
|
34
|
+
"type": "SYSTEM",
|
35
|
+
"name": "last_name",
|
36
|
+
"value": "Drink"
|
37
|
+
}, {
|
38
|
+
"type": "SYSTEM",
|
39
|
+
"name": "email",
|
40
|
+
"value": "anitadrink@example.com"
|
41
|
+
}, {
|
42
|
+
"type": "CUSTOM",
|
43
|
+
"name": "custom_field",
|
44
|
+
"value": "Im a custom field!"
|
45
|
+
}],
|
46
|
+
"campaignStatus": [],
|
47
|
+
"entity_type": "contact_entity",
|
48
|
+
"unsubscribeStatus": [],
|
49
|
+
"emailBounceStatus": []
|
50
|
+
}],
|
51
|
+
"entity_type": "note"
|
52
|
+
}]
|