capsule_crm 1.2.0 → 1.3.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.
Files changed (91) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +15 -0
  3. data/lib/capsule_crm/address.rb +4 -3
  4. data/lib/capsule_crm/associations/belongs_to.rb +2 -1
  5. data/lib/capsule_crm/associations/belongs_to_association.rb +19 -2
  6. data/lib/capsule_crm/associations/belongs_to_finder.rb +46 -0
  7. data/lib/capsule_crm/associations/has_many_association.rb +23 -9
  8. data/lib/capsule_crm/associations/has_many_proxy.rb +38 -4
  9. data/lib/capsule_crm/case.rb +33 -236
  10. data/lib/capsule_crm/configuration.rb +1 -1
  11. data/lib/capsule_crm/connection.rb +3 -0
  12. data/lib/capsule_crm/contactable.rb +1 -1
  13. data/lib/capsule_crm/country.rb +7 -13
  14. data/lib/capsule_crm/currency.rb +7 -13
  15. data/lib/capsule_crm/custom_field.rb +14 -54
  16. data/lib/capsule_crm/custom_field_definition.rb +36 -0
  17. data/lib/capsule_crm/email.rb +4 -3
  18. data/lib/capsule_crm/gettable.rb +11 -0
  19. data/lib/capsule_crm/hash_helper.rb +5 -0
  20. data/lib/capsule_crm/history.rb +37 -252
  21. data/lib/capsule_crm/milestone.rb +9 -9
  22. data/lib/capsule_crm/normalizer.rb +85 -0
  23. data/lib/capsule_crm/opportunity.rb +30 -271
  24. data/lib/capsule_crm/organization.rb +19 -197
  25. data/lib/capsule_crm/party.rb +13 -26
  26. data/lib/capsule_crm/persistence/configuration.rb +25 -0
  27. data/lib/capsule_crm/persistence/deletable.rb +14 -0
  28. data/lib/capsule_crm/persistence/persistable.rb +76 -0
  29. data/lib/capsule_crm/persistence.rb +3 -0
  30. data/lib/capsule_crm/person.rb +19 -194
  31. data/lib/capsule_crm/phone.rb +4 -3
  32. data/lib/capsule_crm/querying/configuration.rb +21 -0
  33. data/lib/capsule_crm/querying/find_all.rb +16 -0
  34. data/lib/capsule_crm/querying/find_one.rb +14 -0
  35. data/lib/capsule_crm/querying/findable.rb +14 -0
  36. data/lib/capsule_crm/querying.rb +4 -0
  37. data/lib/capsule_crm/serializable.rb +38 -0
  38. data/lib/capsule_crm/serializer.rb +54 -7
  39. data/lib/capsule_crm/task.rb +16 -96
  40. data/lib/capsule_crm/track.rb +5 -10
  41. data/lib/capsule_crm/user.rb +3 -15
  42. data/lib/capsule_crm/version.rb +1 -1
  43. data/lib/capsule_crm/website.rb +4 -2
  44. data/lib/capsule_crm.rb +15 -5
  45. data/spec/fabricators/case_fabricator.rb +1 -0
  46. data/spec/fabricators/history_fabricator.rb +1 -0
  47. data/spec/fabricators/opportunity_fabricator.rb +1 -0
  48. data/spec/lib/capsule_crm/associations/belongs_to_finder_spec.rb +48 -0
  49. data/spec/lib/capsule_crm/associations/belongs_to_spec.rb +34 -0
  50. data/spec/lib/capsule_crm/associations/has_many_proxy_spec.rb +54 -14
  51. data/spec/lib/capsule_crm/associations/has_many_spec.rb +15 -2
  52. data/spec/lib/capsule_crm/case_spec.rb +20 -330
  53. data/spec/lib/capsule_crm/country_spec.rb +1 -16
  54. data/spec/lib/capsule_crm/currency_spec.rb +1 -18
  55. data/spec/lib/capsule_crm/custom_field_definition_spec.rb +59 -0
  56. data/spec/lib/capsule_crm/custom_field_spec.rb +2 -27
  57. data/spec/lib/capsule_crm/history_spec.rb +14 -389
  58. data/spec/lib/capsule_crm/milestone_spec.rb +1 -23
  59. data/spec/lib/capsule_crm/normalizer_spec.rb +11 -0
  60. data/spec/lib/capsule_crm/opportunity_spec.rb +22 -341
  61. data/spec/lib/capsule_crm/organization_spec.rb +39 -60
  62. data/spec/lib/capsule_crm/party_spec.rb +37 -59
  63. data/spec/lib/capsule_crm/person_spec.rb +49 -247
  64. data/spec/lib/capsule_crm/serializer_spec.rb +25 -4
  65. data/spec/lib/capsule_crm/task_spec.rb +21 -250
  66. data/spec/lib/capsule_crm/track_spec.rb +1 -15
  67. data/spec/lib/capsule_crm/user_spec.rb +1 -14
  68. data/spec/support/{countries.json → all_countries.json} +0 -0
  69. data/spec/support/{currencies.json → all_currencies.json} +0 -0
  70. data/spec/support/{milestones.json → all_milestones.json} +0 -0
  71. data/spec/support/{tracks.json → all_tracks.json} +0 -0
  72. data/spec/support/custom_field_definitions.json +19 -0
  73. data/spec/support/helpers.rb +3 -2
  74. data/spec/support/no_cases.json +5 -0
  75. data/spec/support/no_countries.json +5 -0
  76. data/spec/support/no_currencies.json +5 -0
  77. data/spec/support/no_milestones.json +5 -0
  78. data/spec/support/no_opportunities.json +5 -0
  79. data/spec/support/no_tasks.json +5 -0
  80. data/spec/support/no_tracks.json +5 -0
  81. data/spec/support/no_users.json +5 -0
  82. data/spec/support/shared_examples/deletable.rb +15 -0
  83. data/spec/support/shared_examples/find_all.rb +34 -0
  84. data/spec/support/shared_examples/find_one.rb +23 -0
  85. data/spec/support/shared_examples/persistable.rb +318 -0
  86. data/spec/support/single_person.json +16 -0
  87. metadata +60 -15
  88. data/lib/capsule_crm/attributes.rb +0 -11
  89. data/lib/capsule_crm/capsule_jsonable.rb +0 -13
  90. data/lib/capsule_crm/collection.rb +0 -9
  91. data/spec/support/single_user.json +0 -25
@@ -7,19 +7,5 @@ describe CapsuleCRM::Track do
7
7
  it { should validate_numericality_of(:id) }
8
8
  end
9
9
 
10
- describe '.all' do
11
- before do
12
- stub_request(:get, /\/api\/tracks$/).
13
- to_return(body: File.read('spec/support/tracks.json'))
14
- end
15
-
16
- subject { CapsuleCRM::Track.all }
17
-
18
- it { expect(subject).to be_a(Array) }
19
- it { expect(subject.length).to eql(2) }
20
- it { expect(subject.first).to be_a(CapsuleCRM::Track) }
21
- it { expect(subject.first.description).to eql('Sales follow up') }
22
- it { expect(subject.first.capture_rule).to eql('OPPORTUNITY') }
23
- it { expect(subject.first.id).to eql(1) }
24
- end
10
+ it_behaves_like 'listable', '/tracks', 'tracks', 2
25
11
  end
@@ -3,18 +3,5 @@ require 'spec_helper'
3
3
  describe CapsuleCRM::User do
4
4
  before { configure }
5
5
 
6
- describe '.all' do
7
- before do
8
- stub_request(:get, /\/api\/users$/).
9
- to_return(body: File.read('spec/support/all_users.json'))
10
- end
11
-
12
- subject { CapsuleCRM::User.all }
13
-
14
- it { should be_a(Array) }
15
-
16
- it { subject.length.should eql(2) }
17
-
18
- it { subject.all? { |item| item.is_a?(CapsuleCRM::User) }.should be_true }
19
- end
6
+ it_behaves_like 'listable', '/users', 'users', 2
20
7
  end
File without changes
File without changes
@@ -0,0 +1,19 @@
1
+ {
2
+ "customFieldDefinitions": {
3
+ "customFieldDefinition": [
4
+ {
5
+ "id": "1",
6
+ "tag": "Staff",
7
+ "label": "NI Number",
8
+ "type": "Text",
9
+ "options": "^[A-CEGHJ-PR-TW-Z]{1}[A-CEGHJ-NPR-TW-Z]{1}[0-9]{6}[A-DFM]{0,1}$"
10
+ },
11
+ {
12
+ "id": "1",
13
+ "label": "Lead Source",
14
+ "type": "List",
15
+ "options": "website;newsletter"
16
+ }
17
+ ]
18
+ }
19
+ }
@@ -1,8 +1,9 @@
1
1
  module Helpers
2
2
  def configure
3
3
  CapsuleCRM.configure do |c|
4
- c.api_token = '1234'
5
- c.subdomain = 'company'
4
+ c.api_token = '1234'
5
+ c.subdomain = 'company'
6
+ c.perform_logging = false
6
7
  end
7
8
  end
8
9
  end
@@ -0,0 +1,5 @@
1
+ {
2
+ "kases": {
3
+ "@size": "0"
4
+ }
5
+ }
@@ -0,0 +1,5 @@
1
+ {
2
+ "countries": {
3
+ "@size": "0"
4
+ }
5
+ }
@@ -0,0 +1,5 @@
1
+ {
2
+ "currencies": {
3
+ "@size": "0"
4
+ }
5
+ }
@@ -0,0 +1,5 @@
1
+ {
2
+ "milestones": {
3
+ "@size": "0"
4
+ }
5
+ }
@@ -0,0 +1,5 @@
1
+ {
2
+ "opportunities": {
3
+ "@size": "0"
4
+ }
5
+ }
@@ -0,0 +1,5 @@
1
+ {
2
+ "tasks": {
3
+ "@size": "0"
4
+ }
5
+ }
@@ -0,0 +1,5 @@
1
+ {
2
+ "tracks": {
3
+ "@size": "0"
4
+ }
5
+ }
@@ -0,0 +1,5 @@
1
+ {
2
+ "users": {
3
+ "@size": "0"
4
+ }
5
+ }
@@ -0,0 +1,15 @@
1
+ shared_examples 'deletable' do
2
+ subject { described_class.new(id: Random.rand(1..10)) }
3
+ before do
4
+ stub_request(:delete, /.*/).to_return(status: 200)
5
+ subject.destroy
6
+ end
7
+
8
+ it 'should not be persisted' do
9
+ expect(subject.persisted?).to eql(false)
10
+ end
11
+
12
+ it 'should not have ID' do
13
+ expect(subject.id).to be_blank
14
+ end
15
+ end
@@ -0,0 +1,34 @@
1
+ shared_examples 'listable' do |path, file_name, item_count|
2
+ before do
3
+ stub_request(:get, /\/api#{path}$/).to_return(body: response)
4
+ end
5
+ subject { described_class.all }
6
+
7
+ context "when there are some #{described_class.to_s.demodulize.downcase.pluralize}" do
8
+ let(:response) { File.read("spec/support/all_#{file_name}.json") }
9
+
10
+ it 'should be an array' do
11
+ expect(subject).to be_a(Array)
12
+ end
13
+
14
+ it "should contain #{item_count} items" do
15
+ expect(subject.length).to eql(item_count)
16
+ end
17
+
18
+ it "should only contain #{described_class} objects" do
19
+ expect(subject.all? { |item| item.is_a?(described_class) }).to eql(true)
20
+ end
21
+ end
22
+
23
+ context "when there are no #{described_class.to_s.demodulize.downcase.pluralize}" do
24
+ let(:response) { File.read("spec/support/no_#{file_name}.json") }
25
+
26
+ it 'should be an array' do
27
+ expect(subject).to be_a(Array)
28
+ end
29
+
30
+ it "should contain 0 items" do
31
+ expect(subject.length).to eql(0)
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,23 @@
1
+ shared_examples 'findable' do |path, id, file_name|
2
+ let(:response) { File.read("spec/support/#{file_name}.json") }
3
+ let(:url) { "https://1234:@company.capsulecrm.com#{path}" }
4
+ before do
5
+ stub_request(:get, url).to_return(body: response)
6
+ end
7
+
8
+ subject { described_class.find(id) }
9
+
10
+ it "should be a #{described_class}" do
11
+ expect(subject).to be_a(described_class)
12
+ end
13
+
14
+ it 'should populate the attributes from the response' do
15
+ attributes.each do |key, value|
16
+ expect(subject.send(key)).to eql(value)
17
+ end
18
+ end
19
+
20
+ it "should return an instance of #{described_class}" do
21
+ expect(subject).to be_a(described_class)
22
+ end
23
+ end
@@ -0,0 +1,318 @@
1
+ shared_examples 'persistable' do |location, id|
2
+ describe '.create' do
3
+ subject { described_class.create attributes }
4
+ before do
5
+ stub_request(:post, /.*/).to_return(headers: { 'Location' => location })
6
+ end
7
+
8
+ it "should return an instance of the #{described_class}" do
9
+ expect(subject).to be_a(described_class)
10
+ end
11
+
12
+ it 'should be persisted' do
13
+ expect(subject).to be_persisted
14
+ end
15
+
16
+ it 'should populate the ID from the response' do
17
+ expect(subject.id).to eql(id)
18
+ end
19
+
20
+ if described_class.attributes.map(&:name).include?(:track_id)
21
+ context "when the #{described_class} has a track" do
22
+ let(:track) { CapsuleCRM::Track.new(id: Random.rand(1..10)) }
23
+ subject do
24
+ described_class.create attributes.merge(track: track)
25
+ end
26
+
27
+ it "should return an instance of #{described_class}" do
28
+ expect(subject).to be_a(described_class)
29
+ end
30
+
31
+ it 'should append the track ID on to the request path' do
32
+ expect(WebMock).to have_requested(
33
+ :post,
34
+ "https://1234:@company.capsulecrm.com#{subject.build_create_path}"
35
+ )
36
+ end
37
+ end
38
+ end
39
+
40
+ context "when the #{described_class} is not valid" do
41
+ subject { described_class.create }
42
+
43
+ it 'should have errors' do
44
+ expect(subject.errors).not_to be_blank
45
+ end
46
+ end
47
+ end
48
+
49
+ describe '.create!' do
50
+ context "when the #{described_class} is valid" do
51
+ subject { described_class.create! attributes }
52
+ before do
53
+ stub_request(:post, /.*/).to_return(headers: { 'Location' => location })
54
+ end
55
+
56
+ it "should be a #{described_class}" do
57
+ expect(subject).to be_a(described_class)
58
+ end
59
+
60
+ it 'should be persisted' do
61
+ expect(subject).to be_persisted
62
+ end
63
+
64
+ it 'should populate the ID from the response' do
65
+ expect(subject.id).to eql(id)
66
+ end
67
+ end
68
+
69
+ context "when the #{described_class} is not valid" do
70
+ subject { described_class.create! }
71
+
72
+ it 'should raise an error' do
73
+ expect { subject }.to raise_error(CapsuleCRM::Errors::RecordInvalid)
74
+ end
75
+ end
76
+ end
77
+
78
+ describe '#update_attributes' do
79
+ context "when the #{described_class} is valid" do
80
+ let(:id) { Random.rand(1..10) }
81
+ subject do
82
+ Fabricate.build(described_class.to_s.demodulize.downcase.to_sym, id: id)
83
+ end
84
+ before do
85
+ stub_request(:put, /.*/).to_return(status: 200)
86
+ subject.update_attributes
87
+ end
88
+
89
+ it 'should keep the ID' do
90
+ expect(subject.id).to eql(id)
91
+ end
92
+
93
+ it 'should send a PUT request' do
94
+ expect(WebMock).to have_requested(
95
+ :put,
96
+ "https://1234:@company.capsulecrm.com#{subject.build_update_path}"
97
+ )
98
+ end
99
+ end
100
+
101
+ context "when the #{described_class} is not valid" do
102
+ subject { described_class.new(id: Random.rand(1..10)) }
103
+ before { subject.update_attributes }
104
+
105
+ it 'should have errors' do
106
+ expect(subject.errors).not_to be_blank
107
+ end
108
+
109
+ it 'should not send a PUT request' do
110
+ expect(WebMock).not_to have_requested(
111
+ :put,
112
+ "https://1234:@company.capsulecrm.com#{subject.build_update_path}"
113
+ )
114
+ end
115
+ end
116
+ end
117
+
118
+ describe '#update_attributes!' do
119
+ context "when the #{described_class} is valid" do
120
+ subject do
121
+ Fabricate.build described_class.to_s.demodulize.downcase.to_sym, id: id
122
+ end
123
+ before do
124
+ stub_request(:put, /.*/).to_return(status: 200)
125
+ subject.update_attributes!
126
+ end
127
+
128
+ it 'should send a PUT request' do
129
+ expect(WebMock).to have_requested(
130
+ :put,
131
+ "https://1234:@company.capsulecrm.com#{subject.build_update_path}"
132
+ )
133
+ end
134
+ end
135
+
136
+ context "when the #{described_class} is not valid" do
137
+ subject { described_class.new(id: Random.rand(1..10)).update_attributes! }
138
+
139
+ it 'should raise an error' do
140
+ expect { subject }.to raise_error(CapsuleCRM::Errors::RecordInvalid)
141
+ end
142
+ end
143
+ end
144
+
145
+ describe '#save' do
146
+ context "when the #{described_class} is a new record" do
147
+ context "when the #{described_class} is valid" do
148
+ before do
149
+ stub_request(:post, /.*/).to_return(headers: {
150
+ 'Location' => location
151
+ }, status: 200)
152
+ end
153
+ subject do
154
+ Fabricate.build described_class.to_s.demodulize.downcase.to_sym
155
+ end
156
+ before { subject.save }
157
+
158
+ it 'should send a POST request' do
159
+ expect(WebMock).to have_requested(
160
+ :post,
161
+ "https://1234:@company.capsulecrm.com#{subject.build_create_path}"
162
+ ).with(body: subject.to_capsule_json)
163
+ end
164
+ end
165
+
166
+ context "when the #{described_class} is not valid" do
167
+ subject { described_class.new }
168
+ before { subject.save }
169
+
170
+ it 'should not send a POST request' do
171
+ expect(WebMock).not_to have_requested(
172
+ :post,
173
+ "https://1234:@company.capsulecrm.com#{subject.build_create_path}"
174
+ )
175
+ end
176
+
177
+ it 'should have errors' do
178
+ expect(subject.errors).not_to be_blank
179
+ end
180
+ end
181
+ end
182
+
183
+ context "when the #{described_class} is not a new record" do
184
+ context "when the #{described_class} is valid" do
185
+ subject do
186
+ Fabricate.build described_class.to_s.demodulize.downcase.to_sym,
187
+ id: Random.rand(1..10)
188
+ end
189
+ before do
190
+ stub_request(:put, /.*/).to_return(status: 200)
191
+ subject.save
192
+ end
193
+
194
+ it 'should send a PUT request' do
195
+ expect(WebMock).to have_requested(
196
+ :put,
197
+ "https://1234:@company.capsulecrm.com#{subject.build_update_path}"
198
+ ).with(subject.to_capsule_json)
199
+ end
200
+ end
201
+
202
+ context "when the #{described_class} is not valid" do
203
+ subject { described_class.new id: Random.rand(1..10) }
204
+ before { subject.save }
205
+
206
+ it 'should not send a PUT request' do
207
+ expect(WebMock).not_to have_requested(
208
+ :put,
209
+ "https://1234:@company.capsulecrm.com#{subject.build_update_path}"
210
+ )
211
+ end
212
+
213
+ it 'should have errors' do
214
+ expect(subject.errors).not_to be_blank
215
+ end
216
+ end
217
+ end
218
+ end
219
+
220
+ describe '#save!' do
221
+ context "when the #{described_class} is a new record" do
222
+ context "when the #{described_class} is not valid" do
223
+ before do
224
+ stub_request(:post, /.*/).to_return(headers: {
225
+ 'Location' => location
226
+ }, status: 200)
227
+ end
228
+ subject do
229
+ Fabricate.build described_class.to_s.demodulize.downcase.to_sym
230
+ end
231
+ before { subject.save }
232
+
233
+ it 'should send a POST request' do
234
+ expect(WebMock).to have_requested(
235
+ :post,
236
+ "https://1234:@company.capsulecrm.com#{subject.build_create_path}"
237
+ ).with(body: subject.to_capsule_json)
238
+ end
239
+
240
+ it 'should populate the ID from the response' do
241
+ expect(subject.id).to eql(id)
242
+ end
243
+ end
244
+
245
+ context "when the #{described_class} is not valid" do
246
+ subject { described_class.new.save! }
247
+
248
+ it 'should raise an error' do
249
+ expect { subject }.to raise_error(CapsuleCRM::Errors::RecordInvalid)
250
+ end
251
+ end
252
+ end
253
+
254
+ context "when the #{described_class} is not a new record" do
255
+ context "when the #{described_class} is valid" do
256
+ subject do
257
+ Fabricate.build described_class.to_s.demodulize.downcase.to_sym,
258
+ id: Random.rand(1..10)
259
+ end
260
+ before do
261
+ stub_request(:put, /.*/).to_return(status: 200)
262
+ subject.save!
263
+ end
264
+
265
+ it 'should send a PUT request' do
266
+ expect(WebMock).to have_requested(
267
+ :put,
268
+ "https://1234:@company.capsulecrm.com#{subject.build_update_path}"
269
+ ).with(body: subject.to_capsule_json)
270
+ end
271
+ end
272
+
273
+ context "when the #{described_class} is not valid" do
274
+ subject { described_class.new(id: Random.rand(1..10)).save! }
275
+
276
+ it 'should raise an error' do
277
+ expect { subject }.to raise_error(CapsuleCRM::Errors::RecordInvalid)
278
+ end
279
+ end
280
+ end
281
+ end
282
+
283
+ describe '#new_record?' do
284
+ context "when the #{described_class} is a new record" do
285
+ subject { described_class.new.new_record? }
286
+
287
+ it 'should be a new record' do
288
+ expect(subject).to eql(true)
289
+ end
290
+ end
291
+
292
+ context "when the #{described_class} is not a new record" do
293
+ subject { described_class.new(id: Random.rand(1..10)).new_record? }
294
+
295
+ it 'should not be a new record' do
296
+ expect(subject).to eql(false)
297
+ end
298
+ end
299
+ end
300
+
301
+ describe '#persisted?' do
302
+ context "when the #{described_class} is persisted" do
303
+ subject { described_class.new.persisted? }
304
+
305
+ it 'should be true' do
306
+ expect(subject).to eql(false)
307
+ end
308
+ end
309
+
310
+ context "when the #{described_class} is not persisted" do
311
+ subject { described_class.new(id: Random.rand(1..10)).persisted? }
312
+
313
+ it 'should not be persisted' do
314
+ expect(subject).to eql(true)
315
+ end
316
+ end
317
+ end
318
+ end
@@ -0,0 +1,16 @@
1
+ {
2
+ "parties": {
3
+ "person": [
4
+ {
5
+ "id": "100",
6
+ "firstName": "Eric",
7
+ "lastName": "Schmidt"
8
+ },
9
+ {
10
+ "id": "101",
11
+ "firstName": "Larry ",
12
+ "lastName": "Page"
13
+ }
14
+ ]
15
+ }
16
+ }