electric_slide 0.5.0 → 0.5.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: 49101ac1f213829f41caab6990cf3183d533f3e1
4
- data.tar.gz: 796e9cd5bbee24fcda95e58022cf1f83f8c4f2f0
2
+ SHA256:
3
+ metadata.gz: c782d5d738f3fb714ff7fb1f3ae8ab63189118ebee98136ceccd9f1d41790526
4
+ data.tar.gz: 253786fe5964259c43a90d0dc2132df9bf1509d75203276f9ef6c6cdfc736eb0
5
5
  SHA512:
6
- metadata.gz: 65de89ec11a21d769a14c50c0a359407b2805b096bf87ca35637502030cbc9bbdc0e3199345ecb52cdea49168d652d0ba19559f02e1edcb379789d9e8f032c3f
7
- data.tar.gz: 9ba4811599997c2831762642cd0e9b49dc98f9606054ef1de8f00448e9c4fe7dc680b0e173aa2ec3cf284bc245ba16c2b67c7d78474e52055c00cbd08503da3d
6
+ metadata.gz: bc24e7ae491fd5d7f3ce5925695d16c6ea9fcb0da4e49ca8258247289a498efcdd17c98a747740c813e8df82dfe368a15ae915b3df19e7b53c404a60b5614e8e
7
+ data.tar.gz: 474108814ec1a811b56c6a8c435d457429a1a447c6901660d1f6dd457c56cc00c4ef2b226e85b19c40fdf90e8ccd93d81243b1c78aea9551c635194c183a19fa
@@ -0,0 +1,27 @@
1
+ name: build
2
+ on:
3
+ - push
4
+ - pull_request
5
+ jobs:
6
+ build:
7
+ strategy:
8
+ fail-fast: false
9
+ matrix:
10
+ ruby: ['2.1', '2.2', '2.3', '2.4', '2.5', '2.6', '2.7', 'jruby-9.1.17', 'jruby-9.2.21']
11
+ runs-on: ubuntu-latest
12
+ steps:
13
+ - name: Checkout
14
+ uses: actions/checkout@v3
15
+
16
+ - name: Setup Ruby
17
+ uses: ruby/setup-ruby@v1
18
+ with:
19
+ ruby-version: ${{ matrix.ruby }}
20
+ bundler: 1.17.3
21
+ bundler-cache: true
22
+
23
+ - name: Create build
24
+ run: bundle exec rake build
25
+
26
+ - name: Run Specs
27
+ run: bundle exec rake spec
data/.gitignore CHANGED
@@ -1,3 +1,4 @@
1
1
  *.swp
2
2
  Gemfile.lock
3
3
  pkg
4
+ .tool-versions
data/CHANGELOG.md CHANGED
@@ -1,4 +1,12 @@
1
1
  # [develop](https://github.com/adhearsion/electric_slide)
2
+ * Bugfix: Conditionally clear agent's call object if the current
3
+ call is the same as the previous call so that call object information
4
+ is not lost when the call goes from queued to connected
5
+
6
+ # [develop](https://github.com/adhearsion/electric_slide)
7
+ * Added `ElectricSlide::CallQueue#update_agent` to safely update a queued agent object's attributes
8
+ * Added `ElectricSlide::Agent#update` to update an agent object's attributes
9
+ * Added `ElectricSlide::Agent#callable?` to check if an agent can be called
2
10
 
3
11
  # [0.5.0](https://github.com/adhearsion/electric_slide/compare/v0.4.2...v0.5.0) - [2016-02-12](https://rubygems.org/gems/adhearsion/versions/0.5.0)
4
12
  * API Breakage: Fixed priority strategy now ensures that the same agent cannot be in multiple priorities
@@ -23,9 +23,9 @@ Gem::Specification.new do |s|
23
23
  s.rubygems_version = "1.2.0"
24
24
  s.summary = "Automatic Call Distributor for Adhearsion"
25
25
 
26
- s.add_runtime_dependency 'adhearsion'
26
+ s.add_runtime_dependency 'adhearsion', ['>= 2.5.0', '< 3.0.0']
27
27
  s.add_runtime_dependency 'countdownlatch'
28
- s.add_runtime_dependency 'activesupport'
28
+ s.add_runtime_dependency 'activesupport', ['<= 3.2.13']
29
29
  s.add_development_dependency 'rspec', ['~> 3.0']
30
30
  s.add_development_dependency 'timecop'
31
31
  s.add_development_dependency 'ci_reporter'
@@ -34,5 +34,9 @@ Gem::Specification.new do |s|
34
34
  s.add_development_dependency 'simplecov'
35
35
  s.add_development_dependency 'simplecov-rcov'
36
36
 
37
+ # These two are needed to keep compatibility with Ruby >= 2.2.0, <= 2.2.3
38
+ s.add_development_dependency 'ruby_dep', ['= 1.3.1']
39
+ s.add_development_dependency 'listen', ['<= 3.1.1']
40
+
37
41
  s.specification_version = 2
38
42
  end
@@ -15,6 +15,22 @@ class ElectricSlide
15
15
  @presence = opts[:presence] || :available
16
16
  end
17
17
 
18
+ # Updates an agent instance's attributes.
19
+ # @param [Hash] opts Agent attributes
20
+ # @return {Agent}
21
+ def update(attrs = {})
22
+ unless Hash === attrs
23
+ raise ArgumentError.new('Agent attributes must be a hash')
24
+ end
25
+
26
+ attrs.each do |attr, value|
27
+ setter = "#{attr}="
28
+ send setter, value
29
+ end
30
+
31
+ self
32
+ end
33
+
18
34
  def update_presence(new_presence, extra_params = {})
19
35
  old_presence = @presence
20
36
  @presence = new_presence
@@ -68,5 +84,10 @@ class ElectricSlide
68
84
  def from
69
85
  @call.from
70
86
  end
87
+
88
+ # Returns `true` if the agent can be called, i.e., has a contact address
89
+ def callable?
90
+ address.present?
91
+ end
71
92
  end
72
93
  end
@@ -165,13 +165,7 @@ class ElectricSlide
165
165
  abort DuplicateAgentError.new("Agent is already in the queue") if get_agent(agent.id)
166
166
 
167
167
  agent.queue = current_actor
168
-
169
- case @connection_type
170
- when :call
171
- abort ArgumentError.new("Agent has no callable address") unless agent.address
172
- when :bridge
173
- bridged_agent_health_check agent
174
- end
168
+ accept_agent! agent
175
169
 
176
170
  logger.info "Adding agent #{agent} to the queue"
177
171
  @agents << agent
@@ -182,6 +176,24 @@ class ElectricSlide
182
176
  async.check_for_connections
183
177
  end
184
178
 
179
+ # Updates a queued agent's attributes
180
+ def update_agent(agent, agent_attrs)
181
+ abort ArgumentError.new('Agent must not be `nil`') unless agent
182
+ unless get_agent(agent.id)
183
+ abort MissingAgentError.new('Agent is not in the queue')
184
+ end
185
+
186
+ # check if the agent is allowed to have the given set of attributes using
187
+ # a dupe, to preserve the state of the original in case of failure
188
+ agent.dup.tap do |double_agent|
189
+ double_agent.update agent_attrs
190
+ accept_agent! double_agent
191
+ end
192
+
193
+ agent.update agent_attrs
194
+ return_agent agent, agent.presence
195
+ end
196
+
185
197
  # Marks an agent as available to take a call. To be called after an agent completes a call
186
198
  # and is ready to take the next call.
187
199
  # @param [Agent] agent The {Agent} that is being returned to the queue
@@ -276,9 +288,10 @@ class ElectricSlide
276
288
  # @param [Agent] agent Agent to be connected
277
289
  # @param [Adhearsion::Call] call Caller to be connected
278
290
  def connect(agent, queued_call)
279
- unless queued_call.active?
291
+ unless queued_call && queued_call.active?
280
292
  logger.warn "Inactive queued call found in #connect"
281
293
  return_agent agent
294
+ return
282
295
  end
283
296
 
284
297
  queued_call[:agent] = agent
@@ -288,15 +301,11 @@ class ElectricSlide
288
301
  when :call
289
302
  call_agent agent, queued_call
290
303
  when :bridge
291
- unless agent.call && agent.call.active?
292
- logger.warn "Inactive agent call found in #connect, returning caller to queue"
293
- priority_enqueue queued_call
294
- end
295
304
  bridge_agent agent, queued_call
296
305
  end
297
306
  rescue *ENDED_CALL_EXCEPTIONS
298
307
  ignoring_ended_calls do
299
- if queued_call.active?
308
+ if queued_call && queued_call.active?
300
309
  logger.warn "Dead call exception in #connect but queued_call still alive, reinserting into queue"
301
310
  priority_enqueue queued_call
302
311
  end
@@ -392,9 +401,9 @@ class ElectricSlide
392
401
 
393
402
  agent_call.on_end do |end_event|
394
403
  # Ensure we don't return an agent that was removed or paused
404
+ old_call = agent.call
395
405
  conditionally_return_agent agent
396
-
397
- agent.call = nil
406
+ agent.call = nil if agent_return_method == :manual || agent.call == old_call
398
407
 
399
408
  agent.callback :disconnect, queue, agent_call, queued_call
400
409
 
@@ -418,6 +427,13 @@ class ElectricSlide
418
427
  end
419
428
 
420
429
  def bridge_agent(agent, queued_call)
430
+ unless agent.call && agent.call.active?
431
+ logger.warn "Inactive agent call found for Agent #{agent.id} while bridging. Logging out agent and returning caller to queue."
432
+ priority_enqueue queued_call
433
+ remove_agent agent
434
+ return
435
+ end
436
+
421
437
  # Stash caller ID to make log messages work even if calls end
422
438
  queued_caller_id = remote_party queued_call
423
439
  agent.call[:queued_call] = queued_call
@@ -455,6 +471,17 @@ class ElectricSlide
455
471
  end
456
472
  end
457
473
 
474
+ def accept_agent!(agent)
475
+ case @connection_type
476
+ when :call
477
+ unless agent.callable?
478
+ abort ArgumentError.new('Agent has no callable address')
479
+ end
480
+ when :bridge
481
+ bridged_agent_health_check agent
482
+ end
483
+ end
484
+
458
485
  # @private
459
486
  def bridged_agent_health_check(agent)
460
487
  if agent.presence == :available && @connection_type == :bridge
@@ -1,4 +1,4 @@
1
1
  # encoding: utf-8
2
2
  class ElectricSlide
3
- VERSION = '0.5.0'
3
+ VERSION = '0.5.1'
4
4
  end
@@ -63,4 +63,56 @@ describe ElectricSlide::Agent do
63
63
  expect(old_presence).to eq :unavailable
64
64
  expect(extra_params[:triggered_by]).to eq 'auto'
65
65
  end
66
+
67
+ describe '#update' do
68
+ it 'returns the agent' do
69
+ expect(subject.update).to eq(subject)
70
+ end
71
+
72
+ context 'when given a hash with an agent attribute as key' do
73
+ it "sets the corresponding agent's attribute to the given value" do
74
+ expect {
75
+ subject.update(address: '456@bar.net')
76
+ }.to change(subject, :address).from('123@foo.com').to('456@bar.net')
77
+ end
78
+ end
79
+
80
+ context 'when given an non-hash argument' do
81
+ it 'raises an error' do
82
+ expect {
83
+ subject.update(nil)
84
+ }.to raise_error(ArgumentError, 'Agent attributes must be a hash')
85
+ end
86
+ end
87
+
88
+ context 'when given a hash with a key that does not correspond to any agent attribute' do
89
+ it "raises an error" do
90
+ expect {
91
+ subject.update(blah: 1)
92
+ }.to raise_error(NoMethodError)
93
+ end
94
+ end
95
+ end
96
+
97
+ describe '#callable?' do
98
+ context 'when the agent has an address' do
99
+ before do
100
+ subject.address = 'Baker St.'
101
+ end
102
+
103
+ it 'returns `true`' do
104
+ expect(subject).to be_callable
105
+ end
106
+ end
107
+
108
+ context 'when the agent has no address' do
109
+ before do
110
+ subject.address = ''
111
+ end
112
+
113
+ it 'returns `false`' do
114
+ expect(subject).to_not be_callable
115
+ end
116
+ end
117
+ end
66
118
  end
@@ -54,6 +54,7 @@ describe ElectricSlide::CallQueue do
54
54
  let(:agent_id) { '123' }
55
55
  let(:agent) { ElectricSlide::Agent.new id: agent_id, address: '123', presence: :available }
56
56
  let!(:agent_call) { Adhearsion::OutboundCall.new }
57
+ let!(:new_agent_call) { Adhearsion::OutboundCall.new }
57
58
  let(:queued_call) { dummy_call }
58
59
  let(:connected_time) { DateTime.now }
59
60
 
@@ -71,6 +72,7 @@ describe ElectricSlide::CallQueue do
71
72
 
72
73
  before do
73
74
  allow(agent_call).to receive(:dial)
75
+ allow(new_agent_call).to receive(:dial)
74
76
  queue.add_agent agent
75
77
  queue.enqueue queued_call
76
78
  end
@@ -101,6 +103,16 @@ describe ElectricSlide::CallQueue do
101
103
  }.to change(agent, :call).from(agent_call).to(nil)
102
104
  end
103
105
 
106
+ it "does not unsets the agent's `call` attribute when the agent connects to another call" do
107
+ queue.remove_agent double_agent # We only want one agent in the queue so they get the next call
108
+ allow(Adhearsion::OutboundCall).to receive(:new) { new_agent_call }
109
+ queue.enqueue dummy_call # We want another call to be waiting after this call
110
+
111
+ expect {
112
+ agent_call << Punchblock::Event::End.new(reason: :hangup)
113
+ }.to change(agent, :call).from(agent_call).to(new_agent_call)
114
+ end
115
+
104
116
  context "when the return strategy is :auto" do
105
117
  let(:agent_return_method) { :auto }
106
118
 
@@ -287,6 +299,54 @@ describe ElectricSlide::CallQueue do
287
299
  end
288
300
  end
289
301
 
302
+ describe '#update_agent' do
303
+ let(:queue) { ElectricSlide::CallQueue.new(connection_type: :call) }
304
+ let(:agent) { ElectricSlide::Agent.new(id: '1', address: 'agent@example.com', presence: :on_call) }
305
+
306
+ before do
307
+ queue.add_agent(agent)
308
+ end
309
+
310
+ it 'updates the agent with the given attributes' do
311
+ expect {
312
+ queue.update_agent(agent, address: 'reagent@acme.com')
313
+ }.to change(agent, :address).from('agent@example.com').to('reagent@acme.com')
314
+ end
315
+
316
+ it 'returns the agent to the queue' do
317
+ expect(queue.wrapped_object).to receive(:return_agent).with(agent, :on_call)
318
+ queue.update_agent(agent, address: 'reagent@acme.com')
319
+ end
320
+
321
+ context 'when given a set of attributes that makes the agent unacceptable in the queue' do
322
+ it 'raises an error' do
323
+ expect {
324
+ queue.update_agent(agent, address: '')
325
+ }.to raise_error(ArgumentError, 'Agent has no callable address')
326
+ end
327
+
328
+ it "does not change the agent's attributes" do
329
+ expect { queue.update_agent(agent, address: '') }.to raise_error(ArgumentError)
330
+
331
+ expect(agent.id).to eq('1')
332
+ expect(agent.address).to eq('agent@example.com')
333
+ expect(agent.presence).to eq(:on_call)
334
+ end
335
+ end
336
+
337
+ context 'when given an agent not in the queue' do
338
+ before do
339
+ queue.remove_agent agent
340
+ end
341
+
342
+ it 'raises an error' do
343
+ expect {
344
+ queue.update_agent(agent, address: 'ghost@imf.com')
345
+ }.to raise_error(ElectricSlide::CallQueue::MissingAgentError, 'Agent is not in the queue')
346
+ end
347
+ end
348
+ end
349
+
290
350
  describe '#return_agent' do
291
351
  let(:queue) { ElectricSlide::CallQueue.new }
292
352
  let(:agent) { ElectricSlide::Agent.new(id: '1', address: 'agent@example.com', presence: :on_call) }
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: electric_slide
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.5.0
4
+ version: 0.5.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ben Klang
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2016-02-12 00:00:00.000000000 Z
11
+ date: 2022-11-10 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: adhearsion
@@ -16,14 +16,20 @@ dependencies:
16
16
  requirements:
17
17
  - - ">="
18
18
  - !ruby/object:Gem::Version
19
- version: '0'
19
+ version: 2.5.0
20
+ - - "<"
21
+ - !ruby/object:Gem::Version
22
+ version: 3.0.0
20
23
  type: :runtime
21
24
  prerelease: false
22
25
  version_requirements: !ruby/object:Gem::Requirement
23
26
  requirements:
24
27
  - - ">="
25
28
  - !ruby/object:Gem::Version
26
- version: '0'
29
+ version: 2.5.0
30
+ - - "<"
31
+ - !ruby/object:Gem::Version
32
+ version: 3.0.0
27
33
  - !ruby/object:Gem::Dependency
28
34
  name: countdownlatch
29
35
  requirement: !ruby/object:Gem::Requirement
@@ -42,16 +48,16 @@ dependencies:
42
48
  name: activesupport
43
49
  requirement: !ruby/object:Gem::Requirement
44
50
  requirements:
45
- - - ">="
51
+ - - "<="
46
52
  - !ruby/object:Gem::Version
47
- version: '0'
53
+ version: 3.2.13
48
54
  type: :runtime
49
55
  prerelease: false
50
56
  version_requirements: !ruby/object:Gem::Requirement
51
57
  requirements:
52
- - - ">="
58
+ - - "<="
53
59
  - !ruby/object:Gem::Version
54
- version: '0'
60
+ version: 3.2.13
55
61
  - !ruby/object:Gem::Dependency
56
62
  name: rspec
57
63
  requirement: !ruby/object:Gem::Requirement
@@ -150,6 +156,34 @@ dependencies:
150
156
  - - ">="
151
157
  - !ruby/object:Gem::Version
152
158
  version: '0'
159
+ - !ruby/object:Gem::Dependency
160
+ name: ruby_dep
161
+ requirement: !ruby/object:Gem::Requirement
162
+ requirements:
163
+ - - '='
164
+ - !ruby/object:Gem::Version
165
+ version: 1.3.1
166
+ type: :development
167
+ prerelease: false
168
+ version_requirements: !ruby/object:Gem::Requirement
169
+ requirements:
170
+ - - '='
171
+ - !ruby/object:Gem::Version
172
+ version: 1.3.1
173
+ - !ruby/object:Gem::Dependency
174
+ name: listen
175
+ requirement: !ruby/object:Gem::Requirement
176
+ requirements:
177
+ - - "<="
178
+ - !ruby/object:Gem::Version
179
+ version: 3.1.1
180
+ type: :development
181
+ prerelease: false
182
+ version_requirements: !ruby/object:Gem::Requirement
183
+ requirements:
184
+ - - "<="
185
+ - !ruby/object:Gem::Version
186
+ version: 3.1.1
153
187
  description: Automatic Call Distributor (ACD) for Adhearsion. Currently implements
154
188
  only Round Robin distribution strategies.
155
189
  email: dev&adhearsion.com
@@ -157,10 +191,10 @@ executables: []
157
191
  extensions: []
158
192
  extra_rdoc_files: []
159
193
  files:
194
+ - ".github/workflows/build.yml"
160
195
  - ".gitignore"
161
196
  - ".lgtm"
162
197
  - ".rspec"
163
- - ".travis.yml"
164
198
  - CHANGELOG.md
165
199
  - Gemfile
166
200
  - Guardfile
@@ -201,8 +235,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
201
235
  - !ruby/object:Gem::Version
202
236
  version: '0'
203
237
  requirements: []
204
- rubyforge_project:
205
- rubygems_version: 2.5.1
238
+ rubygems_version: 3.1.6
206
239
  signing_key:
207
240
  specification_version: 2
208
241
  summary: Automatic Call Distributor for Adhearsion
data/.travis.yml DELETED
@@ -1,10 +0,0 @@
1
- language: ruby
2
- rvm:
3
- - 2.2.0
4
- - 2.2.1
5
- - 2.2.2
6
- - jruby-9.0.0.0
7
- matrix:
8
- allow_failures:
9
- - rvm: ruby-head
10
- sudo: false