capsule_crm 1.2.0 → 1.3.0

Sign up to get free protection for your applications and to get access to all the features.
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
+ }