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.
@@ -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}&note=#{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,3 @@
1
+ module AgileCRMWrapper
2
+ VERSION = '1.0.0'
3
+ end
@@ -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,11 @@
1
+ require 'spec_helper'
2
+
3
+ describe AgileCRMWrapper::Note do
4
+ describe '.create' do
5
+ it 'creates a new note' do
6
+ expect(
7
+ AgileCRMWrapper::Note.create(123, subject: '', description: '')
8
+ ).to be_kind_of(AgileCRMWrapper::Note)
9
+ end
10
+ end
11
+ 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
+ }]