asterisk-ajam 0.0.2 → 0.1.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 +6 -14
- data/.github/copilot-instructions.md +55 -0
- data/.travis.yml +1 -1
- data/Gemfile +2 -3
- data/README.md +30 -24
- data/Rakefile +8 -8
- data/asterisk-ajam.gemspec +16 -16
- data/example/ajam.rb +9 -9
- data/lib/asterisk/ajam/response.rb +61 -53
- data/lib/asterisk/ajam/session.rb +86 -76
- data/lib/asterisk/ajam/version.rb +1 -1
- data/lib/asterisk/ajam.rb +0 -1
- data/spec/asterisk/ajam/response_spec.rb +43 -43
- data/spec/asterisk/ajam/session_spec.rb +50 -53
- data/spec/spec_helper.rb +17 -17
- metadata +22 -25
checksums.yaml
CHANGED
|
@@ -1,15 +1,7 @@
|
|
|
1
1
|
---
|
|
2
|
-
|
|
3
|
-
metadata.gz:
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
metadata.gz: !binary |-
|
|
9
|
-
YjRmYjY2ODMwMzFmNDNkYTA1MjBmYjk0M2E2NTg1MzBmOGViZDRjYmVjMDYz
|
|
10
|
-
NWZlMmFiMzk5OTNmZWMyZDJiYmQwNTlmZjFjOWRhNDdiM2QyOTA2NTIxY2Qw
|
|
11
|
-
NjY0NWMyYTQzOGI5YzJlZWJkNjdiZDQxNzAwMzgwMTViNzdjMGE=
|
|
12
|
-
data.tar.gz: !binary |-
|
|
13
|
-
NmE3ZDUyNGU3NjdhYTI1NDY0OGFlYmI1OGY5ZWJhNjM5ZjhhZTM5ZmU1Y2Y0
|
|
14
|
-
YWRkYzcyZmMxMTczYTM0YzRkOTA2Zjc3MmEwYmEwZWUwYTA3ZDgyZGZmNjk2
|
|
15
|
-
MTRkZTk5YjRiZGE0Y2RmNjA4YzMxYTRmMGE3N2NhMzlhNjczNzM=
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: 6c38257b070aaeea398c8645c0335c23e0aeb29e87db6e65f598cc054eeaff72
|
|
4
|
+
data.tar.gz: 635ce04b15079e19b59f2339e6befc4b17584d7490f17e94e3143dfe2110b35c
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: b3bfc0dfc1152a4b5bdaf7528cc87f7abf379d96a467e84a22f6f49af498794ef8c91fc2732900fa16d46d3e9eb8fd9b2947473c94bab6ebf589e4a44c7bbf5e
|
|
7
|
+
data.tar.gz: 5d44099886152400b9e7242bd44688d07595632420689c85247ec0f462ba3398553ebb1ac41ee08280db2eb964e315623d41004b72a569c21d0970e9375608d0
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
# Copilot Instructions
|
|
2
|
+
|
|
3
|
+
## Build & Test Commands
|
|
4
|
+
|
|
5
|
+
```bash
|
|
6
|
+
bundle install # install dependencies
|
|
7
|
+
rake spec # run all tests (default task)
|
|
8
|
+
rake spec_doc # run tests with documentation formatter
|
|
9
|
+
rspec spec/asterisk/ajam/session_spec.rb # run a single spec file
|
|
10
|
+
rspec spec/asterisk/ajam/response_spec.rb # run a single spec file
|
|
11
|
+
rake rdoc # generate RDoc documentation
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
## Publishing to RubyGems
|
|
15
|
+
|
|
16
|
+
```bash
|
|
17
|
+
# One-time: authenticate with your RubyGems account
|
|
18
|
+
gem signin
|
|
19
|
+
# Or manually fetch and store the API key:
|
|
20
|
+
# curl -u USERNAME https://rubygems.org/api/v1/api_key.yaml > ~/.gem/credentials && chmod 0600 ~/.gem/credentials
|
|
21
|
+
|
|
22
|
+
# Bump version in lib/asterisk/ajam/version.rb, then:
|
|
23
|
+
rake build # builds pkg/asterisk-ajam-X.Y.Z.gem
|
|
24
|
+
rake release # builds, tags vX.Y.Z in git, pushes tag, and publishes to rubygems.org
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
`rake release` requires a clean git working tree and that the tag doesn't already exist.
|
|
28
|
+
|
|
29
|
+
## Architecture
|
|
30
|
+
|
|
31
|
+
This is a Ruby gem (`asterisk-ajam`) that communicates with the Asterisk PBX [AMI](https://wiki.asterisk.org/wiki/display/AST/Asterisk+11+AMI+Actions) over HTTP(S) via [AJAM](https://wiki.asterisk.org/wiki/pages/viewpage.action?pageId=4817256).
|
|
32
|
+
|
|
33
|
+
**Entry point:** `Asterisk::AJAM.connect(options)` in `lib/asterisk/ajam.rb` — creates and logs in a `Session`, returning it.
|
|
34
|
+
|
|
35
|
+
**Two core classes:**
|
|
36
|
+
|
|
37
|
+
- `Asterisk::AJAM::Session` (`lib/asterisk/ajam/session.rb`) — manages the HTTP connection, login, session cookie, and dispatches actions. Uses `method_missing` to implement `action_NAME` dynamic methods that map to AMI action names.
|
|
38
|
+
- `Asterisk::AJAM::Response` (`lib/asterisk/ajam/response.rb`) — wraps `Net::HTTPResponse`, parses the AJAM XML body using `libxml-ruby`, and exposes:
|
|
39
|
+
- `#list` — array of hashes for eventlist responses (e.g., SIP peers)
|
|
40
|
+
- `#attribute` — hash of attributes for single-result responses
|
|
41
|
+
- `#data` — string output for `command` responses (`opaque_data`)
|
|
42
|
+
- `#session_id` — parsed from `Set-Cookie` header (`mansession_id`)
|
|
43
|
+
|
|
44
|
+
**Request flow:** `Session#send_action` → `http_send_action` → `Net::HTTP::Post` with form data → `Response.new`. Session cookies (`mansession_id`) are passed back on every subsequent request via `request_headers`.
|
|
45
|
+
|
|
46
|
+
**Custom exceptions** defined in `session.rb`: `InvalidURI`, `InvalidAMILogin`, `NotLoggedIn`. In `response.rb`: `InvalidHTTPBody`.
|
|
47
|
+
|
|
48
|
+
## Key Conventions
|
|
49
|
+
|
|
50
|
+
- **Dynamic action methods:** `action_NAME` calls are handled via `method_missing`. Any method matching `/^action_\w+$/` is dispatched to `send_action` with the suffix as the AMI action symbol (e.g., `action_sippeers` → `send_action(:sippeers, {})`).
|
|
51
|
+
- **SSL:** Enabled automatically when URI scheme is `https`. Certificate verification is intentionally disabled (`VERIFY_NONE`).
|
|
52
|
+
- **Specs use modern RSpec expectations** (`expect(...).to receive(...)`) rather than the older RSpec 2 `.stub(...)` / `.should_receive(...)` syntax.
|
|
53
|
+
- **Fixture helpers** for HTTP responses are defined as top-level methods in `spec/spec_helper.rb` (e.g., `get_body_sippeers`, `cmd_body_dialplan_reload`).
|
|
54
|
+
- **XML format:** AJAM responses are `<ajax-response>` documents; each `<generic>` element maps to one node. The first node is the status node (checked for `response='Success'` or `response='Follows'`); subsequent nodes form the event list.
|
|
55
|
+
- Runtime dependency: `libxml-ruby` gem (used in `Response`).
|
data/.travis.yml
CHANGED
data/Gemfile
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
source 'https://rubygems.org'
|
|
2
2
|
|
|
3
|
-
gem '
|
|
4
|
-
gem '
|
|
5
|
-
# Specify your gem's dependencies in asterisk-ajam.gemspec
|
|
3
|
+
gem 'coveralls', require: false
|
|
4
|
+
gem 'simplecov', require: false, group: :test
|
|
6
5
|
gemspec
|
data/README.md
CHANGED
|
@@ -1,16 +1,17 @@
|
|
|
1
1
|
# Asterisk::AJAM
|
|
2
|
+
|
|
2
3
|
[](https://travis-ci.org/staskobzar/asterisk-ajam)
|
|
3
4
|
[](https://coveralls.io/r/staskobzar/asterisk-ajam?branch=master)
|
|
4
5
|
[](http://badge.fury.io/rb/asterisk-ajam)
|
|
5
6
|
|
|
6
|
-
|
|
7
|
-
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
This is a very simple library that allows to communicate with [Asterisk PBX](http://www.asterisk.org/) using [Asterisk Management Interface (AMI) actions](https://wiki.asterisk.org/wiki/display/AST/Asterisk+11+AMI+Actions).
|
|
8
10
|
Library communicates with AMI through HTTP(s) protocol. HTTP interface is provided by Asterisk PBX and called [AJAM](https://wiki.asterisk.org/wiki/pages/viewpage.action?pageId=4817256)
|
|
9
11
|
|
|
10
12
|
This library does not provide events interface to AMI. It only sends actions/commands and read responses back. If you need real AMI interface please, check [Adhearsion framework](http://adhearsion.com/) which is very powerful and provides a lot of functionality.
|
|
11
13
|
|
|
12
|
-
This library can use
|
|
13
|
-
|
|
14
|
+
This library can use HTTP Authentication username/password for AJAM servers behind proxy protected with basic access authentication scheme and HTTPS (SSL).
|
|
14
15
|
|
|
15
16
|
## Installation
|
|
16
17
|
|
|
@@ -20,63 +21,66 @@ Add this line to your application's Gemfile:
|
|
|
20
21
|
|
|
21
22
|
And then execute:
|
|
22
23
|
|
|
23
|
-
|
|
24
|
+
bundle install
|
|
24
25
|
|
|
25
26
|
Or install it yourself as:
|
|
26
27
|
|
|
27
|
-
|
|
28
|
+
gem install asterisk-ajam
|
|
28
29
|
|
|
29
30
|
## Usage
|
|
30
31
|
|
|
31
32
|
You can check file [example/ajam.rb](https://github.com/staskobzar/asterisk-ajam/blob/master/example/ajam.rb) for some examples (comes with source).
|
|
32
33
|
|
|
33
34
|
Simple example:
|
|
35
|
+
|
|
34
36
|
```ruby
|
|
35
37
|
require 'asterisk/ajam'
|
|
36
38
|
ajam = Asterisk::AJAM.connect :uri => 'http://127.0.0.1:8088/mxml',
|
|
37
39
|
:ami_user => 'admin',
|
|
38
40
|
:ami_password => 'passw0rd'
|
|
39
41
|
if ajam.connected?
|
|
40
|
-
|
|
42
|
+
ajam.command 'dialplan reload'
|
|
41
43
|
res = ajam.action_sippeers
|
|
42
44
|
res.list.each {|peer| puts peer['objectname']}
|
|
43
45
|
end
|
|
44
46
|
```
|
|
45
47
|
|
|
46
|
-
List of argument options for
|
|
48
|
+
List of argument options for `Asterisk::AJAM.connect`:
|
|
47
49
|
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
50
|
+
- `:uri` URI of AJAM server. See [Asterisk documentation](https://wiki.asterisk.org/wiki/pages/viewpage.action?pageId=4817256) which explains how to configure AJAM server.
|
|
51
|
+
- `:ami_user` AMI user username
|
|
52
|
+
- `:ami_password` AMI user password
|
|
53
|
+
- `:proxy_user` proxy username for HTTP Authentication
|
|
54
|
+
- `:proxy_pass` proxy password for HTTP Authentication
|
|
53
55
|
|
|
54
56
|
If URI scheme is "https" then library enables SSL use. Please, note that current version does not enable verify mode of Net::HTTPS ruby library so it will accept any SSL certiificates without verifications.
|
|
55
57
|
|
|
56
|
-
|
|
58
|
+
`Asterisk::AJAM.connect` returns `Asterisk::AJAM::Session` class which provides several useful methods:
|
|
59
|
+
|
|
60
|
+
`connected?` returns true if successfully logged in AMI
|
|
57
61
|
|
|
58
|
-
|
|
62
|
+
`command` send command to AMI and read response. Example:
|
|
59
63
|
|
|
60
|
-
```command``` send command to AMI and read response. Example:
|
|
61
64
|
```ruby
|
|
62
65
|
ajam.command "dialplan reload"
|
|
63
66
|
ajam.command "core restart when convenient"
|
|
64
67
|
```
|
|
65
68
|
|
|
66
|
-
|
|
69
|
+
`action_NAME` sends action to AMI. NAME must be replaced with AMI action name (see: [Asterisk documentation](https://wiki.asterisk.org/wiki/display/AST/Asterisk+11+AMI+Actions)). Accepts hash as an argument. Example:
|
|
70
|
+
|
|
67
71
|
```ruby
|
|
68
72
|
ajam.action_agents # no arguments
|
|
69
73
|
ajam.action_mailboxcount mailbox: "5555@default"
|
|
70
74
|
ajam.action_originate :channel => 'Local/local-chan@from-pstn',
|
|
71
|
-
|
|
72
|
-
|
|
75
|
+
:application => 'MusicOnHold',
|
|
76
|
+
:callerid => 'John Doe <5555555>'
|
|
73
77
|
```
|
|
74
78
|
|
|
75
|
-
Actions and Commands return
|
|
79
|
+
Actions and Commands return `Asterisk::AJAM::Response` class with several methods where response data can be found:
|
|
76
80
|
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
81
|
+
- list
|
|
82
|
+
- attribute
|
|
83
|
+
- data
|
|
80
84
|
|
|
81
85
|
```ruby
|
|
82
86
|
res = ajam.action_meetmelist
|
|
@@ -86,6 +90,7 @@ pp res.data
|
|
|
86
90
|
```
|
|
87
91
|
|
|
88
92
|
## Using Apache proxy tutorial
|
|
93
|
+
|
|
89
94
|
As an additional security measure Apache proxy with HTTP Authentication can be used. Here is an example of Apache Virtual Host configuration as Proxy to AJAM server
|
|
90
95
|
|
|
91
96
|
```
|
|
@@ -106,11 +111,13 @@ NameVirtualHost *:8888
|
|
|
106
111
|
```
|
|
107
112
|
|
|
108
113
|
Generate password file:
|
|
114
|
+
|
|
109
115
|
```
|
|
110
116
|
htpasswd -b -c /etc/httpd/.htpasswd admin passw0rd
|
|
111
117
|
```
|
|
112
118
|
|
|
113
119
|
Connect to proxy:
|
|
120
|
+
|
|
114
121
|
```ruby
|
|
115
122
|
ajam = Asterisk::AJAM.connect :uri => 'http://ajamproxy.com:8888/mxml',
|
|
116
123
|
:ami_user => 'admin',
|
|
@@ -126,4 +133,3 @@ ajam = Asterisk::AJAM.connect :uri => 'http://ajamproxy.com:8888/mxml',
|
|
|
126
133
|
3. Commit your changes (`git commit -am 'Add some feature'`)
|
|
127
134
|
4. Push to the branch (`git push origin my-new-feature`)
|
|
128
135
|
5. Create new Pull Request
|
|
129
|
-
|
data/Rakefile
CHANGED
|
@@ -1,26 +1,26 @@
|
|
|
1
|
-
require
|
|
1
|
+
require 'bundler/gem_tasks'
|
|
2
2
|
require 'rspec/core/rake_task'
|
|
3
3
|
require 'rdoc/task'
|
|
4
4
|
|
|
5
5
|
RSpec::Core::RakeTask.new(:spec) do |t|
|
|
6
|
-
t.rspec_opts =
|
|
6
|
+
t.rspec_opts = '--color'
|
|
7
7
|
end
|
|
8
8
|
|
|
9
9
|
RSpec::Core::RakeTask.new(:spec_doc) do |t|
|
|
10
|
-
t.rspec_opts =
|
|
10
|
+
t.rspec_opts = '--color --format doc'
|
|
11
11
|
end
|
|
12
12
|
|
|
13
13
|
RSpec::Core::RakeTask.new(:spec_html) do |t|
|
|
14
|
-
t.rspec_opts =
|
|
14
|
+
t.rspec_opts = '--format html -o spec/reports/index.html'
|
|
15
15
|
end
|
|
16
16
|
|
|
17
17
|
Rake::RDocTask.new do |rd|
|
|
18
|
-
rd.main =
|
|
19
|
-
rd.rdoc_files.include(
|
|
18
|
+
rd.main = 'README.md'
|
|
19
|
+
rd.rdoc_files.include('README.md', 'lib/**/*.rb')
|
|
20
20
|
rd.title = 'Asterisk::AJAM: Ruby module for interacting with Asterisk management interface (AMI) via HTTP'
|
|
21
|
-
rd.rdoc_dir =
|
|
21
|
+
rd.rdoc_dir = 'doc'
|
|
22
22
|
rd.options << '--line-numbers'
|
|
23
23
|
rd.options << '--all'
|
|
24
24
|
end
|
|
25
25
|
|
|
26
|
-
task :
|
|
26
|
+
task default: :spec
|
data/asterisk-ajam.gemspec
CHANGED
|
@@ -1,26 +1,26 @@
|
|
|
1
|
-
|
|
2
|
-
lib = File.expand_path('../lib', __FILE__)
|
|
1
|
+
lib = File.expand_path('lib', __dir__)
|
|
3
2
|
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
|
4
3
|
require 'asterisk/ajam/version'
|
|
5
4
|
|
|
6
5
|
Gem::Specification.new do |spec|
|
|
7
|
-
spec.name =
|
|
6
|
+
spec.name = 'asterisk-ajam'
|
|
8
7
|
spec.version = Asterisk::AJAM::VERSION
|
|
9
|
-
spec.authors = [
|
|
10
|
-
spec.email = [
|
|
11
|
-
spec.description =
|
|
12
|
-
spec.summary =
|
|
13
|
-
spec.homepage =
|
|
14
|
-
spec.license =
|
|
8
|
+
spec.authors = ['Stas Kobzar']
|
|
9
|
+
spec.email = ['staskobzar@gmail.com']
|
|
10
|
+
spec.description = 'Ruby module for interacting with Asterisk management interface (AMI) via HTTP'
|
|
11
|
+
spec.summary = 'AMI via HTTP'
|
|
12
|
+
spec.homepage = 'https://github.com/staskobzar/asterisk-ajam'
|
|
13
|
+
spec.license = 'MIT'
|
|
14
|
+
|
|
15
|
+
spec.required_ruby_version = '>= 3.2.0'
|
|
15
16
|
|
|
16
17
|
spec.files = `git ls-files`.split($/)
|
|
17
18
|
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
|
18
|
-
spec.require_paths = [
|
|
19
|
+
spec.require_paths = ['lib']
|
|
19
20
|
|
|
20
|
-
spec.add_development_dependency
|
|
21
|
-
spec.add_development_dependency
|
|
22
|
-
spec.add_development_dependency
|
|
23
|
-
spec.add_development_dependency
|
|
24
|
-
|
|
25
|
-
spec.add_runtime_dependency "libxml-ruby"
|
|
21
|
+
spec.add_development_dependency 'bundler'
|
|
22
|
+
spec.add_development_dependency 'rake'
|
|
23
|
+
spec.add_development_dependency 'rdoc'
|
|
24
|
+
spec.add_development_dependency 'rspec'
|
|
25
|
+
spec.add_runtime_dependency 'libxml-ruby'
|
|
26
26
|
end
|
data/example/ajam.rb
CHANGED
|
@@ -1,21 +1,21 @@
|
|
|
1
1
|
#!/usr/bin/env ruby
|
|
2
2
|
require 'asterisk/ajam'
|
|
3
3
|
require 'pp'
|
|
4
|
-
ajam = Asterisk::AJAM.connect :
|
|
5
|
-
:
|
|
6
|
-
:
|
|
7
|
-
:
|
|
8
|
-
:
|
|
4
|
+
ajam = Asterisk::AJAM.connect uri: 'http://127.0.0.1:8088/mxml',
|
|
5
|
+
ami_user: 'admin',
|
|
6
|
+
ami_password: 'amp111',
|
|
7
|
+
proxy_user: 'admin',
|
|
8
|
+
proxy_pass: 'admin'
|
|
9
9
|
|
|
10
10
|
pp ajam.connected?
|
|
11
11
|
# actions
|
|
12
12
|
res = ajam.action_sippeers
|
|
13
13
|
pp res.list
|
|
14
14
|
|
|
15
|
-
ajam = Asterisk::AJAM.connect :
|
|
16
|
-
:
|
|
17
|
-
:
|
|
18
|
-
:
|
|
15
|
+
ajam = Asterisk::AJAM.connect uri: 'https://127.0.0.1:9443/mxml',
|
|
16
|
+
ami_user: 'admin',
|
|
17
|
+
ami_password: 'amp111',
|
|
18
|
+
use_ssl: true
|
|
19
19
|
res = ajam.action_corestatus
|
|
20
20
|
pp res.attribute
|
|
21
21
|
pp res.list
|
|
@@ -1,4 +1,8 @@
|
|
|
1
|
-
|
|
1
|
+
begin
|
|
2
|
+
require 'libxml-ruby'
|
|
3
|
+
rescue LoadError
|
|
4
|
+
require 'libxml'
|
|
5
|
+
end
|
|
2
6
|
|
|
3
7
|
#
|
|
4
8
|
# = asterisk/ajam/response.rb
|
|
@@ -6,15 +10,14 @@ require 'libxml'
|
|
|
6
10
|
module Asterisk
|
|
7
11
|
module AJAM
|
|
8
12
|
# Exception raised when HTTP response body is invalid
|
|
9
|
-
class InvalidHTTPBody < StandardError;end
|
|
13
|
+
class InvalidHTTPBody < StandardError; end # :nodoc:
|
|
10
14
|
|
|
11
15
|
#
|
|
12
|
-
# Generic class to process and store responses from
|
|
16
|
+
# Generic class to process and store responses from
|
|
13
17
|
# Asterisk AJAM server. Stores data from HTTP response
|
|
14
18
|
# and xml document received from Asterisk server
|
|
15
19
|
#
|
|
16
20
|
class Response
|
|
17
|
-
|
|
18
21
|
# HTTP response code
|
|
19
22
|
attr_reader :code
|
|
20
23
|
|
|
@@ -27,15 +30,18 @@ module Asterisk
|
|
|
27
30
|
# Response attributes
|
|
28
31
|
attr_reader :attribute
|
|
29
32
|
|
|
30
|
-
# Creates new Response class instance. Sets instance
|
|
33
|
+
# Creates new Response class instance. Sets instance
|
|
31
34
|
# variables from HTTP Response (like code). Parses body.
|
|
32
35
|
def initialize(http)
|
|
33
|
-
|
|
34
|
-
|
|
36
|
+
unless http.is_a?(Net::HTTPResponse)
|
|
37
|
+
raise ArgumentError,
|
|
38
|
+
"Expected Net::HTTPResponse. Got #{http.class}"
|
|
39
|
+
end
|
|
35
40
|
@attribute = []
|
|
36
41
|
@list = []
|
|
37
42
|
@code = http.code.to_i
|
|
38
43
|
return unless httpok?
|
|
44
|
+
|
|
39
45
|
parse_body http.body
|
|
40
46
|
set_session_id http
|
|
41
47
|
end
|
|
@@ -56,64 +62,66 @@ module Asterisk
|
|
|
56
62
|
end
|
|
57
63
|
|
|
58
64
|
# raw xml from command response
|
|
59
|
-
|
|
60
|
-
@raw_xml
|
|
61
|
-
end
|
|
65
|
+
attr_reader :raw_xml
|
|
62
66
|
|
|
63
67
|
private
|
|
64
|
-
# Parses HTTP response body.
|
|
65
|
-
# Body should by xml string. Otherwise will raise
|
|
66
|
-
# exception InvalidHTTPBody
|
|
67
|
-
def parse_body xml
|
|
68
|
-
@raw_xml = xml
|
|
69
|
-
set_nodes xml
|
|
70
|
-
verify_response
|
|
71
|
-
set_eventlist
|
|
72
|
-
end
|
|
73
68
|
|
|
74
|
-
|
|
75
|
-
|
|
69
|
+
# Parses HTTP response body.
|
|
70
|
+
# Body should by xml string. Otherwise will raise
|
|
71
|
+
# exception InvalidHTTPBody
|
|
72
|
+
def parse_body(xml)
|
|
73
|
+
@raw_xml = xml
|
|
74
|
+
set_nodes xml
|
|
75
|
+
verify_response
|
|
76
|
+
set_eventlist
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
# parse xml body and set result to internal variable for farther processing
|
|
80
|
+
def set_nodes(xml)
|
|
81
|
+
if xml.to_s.empty?
|
|
76
82
|
raise InvalidHTTPBody,
|
|
77
|
-
|
|
78
|
-
src = LibXML::XML::Parser.string(xml).parse
|
|
79
|
-
@nodes = src.root.find('response/generic').to_a
|
|
83
|
+
'Empty response body'
|
|
80
84
|
end
|
|
85
|
+
src = LibXML::XML::Parser.string(xml).parse
|
|
86
|
+
@nodes = src.root.find('response/generic').to_a
|
|
87
|
+
end
|
|
81
88
|
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
+
#
|
|
90
|
+
# Check if AJAM response is successful and set internal variable
|
|
91
|
+
# @success
|
|
92
|
+
def verify_response
|
|
93
|
+
node = @nodes.shift
|
|
94
|
+
@success = %w[success follows].include? node[:response].to_s.downcase
|
|
95
|
+
@attribute = node.attributes.to_h
|
|
89
96
|
|
|
90
|
-
|
|
91
|
-
|
|
97
|
+
set_command_data
|
|
98
|
+
end
|
|
92
99
|
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
@session_id = $2
|
|
97
|
-
end
|
|
98
|
-
end
|
|
100
|
+
# extract mansession_id from cookies
|
|
101
|
+
def set_session_id(http)
|
|
102
|
+
return unless /mansession_id=(['"])([^'"]+)\1/ =~ http['Set-Cookie']
|
|
99
103
|
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
end
|
|
108
|
-
end
|
|
104
|
+
@session_id = ::Regexp.last_match(2)
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
# for responses that contain eventlist of values set it to
|
|
108
|
+
# internal attributes
|
|
109
|
+
def set_eventlist
|
|
110
|
+
return unless @attribute['eventlist'].to_s.downcase.eql? 'start'
|
|
109
111
|
|
|
110
|
-
|
|
111
|
-
if
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
end
|
|
112
|
+
@nodes.each do |node|
|
|
113
|
+
next if node['eventlist'].eql? 'Complete'
|
|
114
|
+
|
|
115
|
+
@list << node.attributes.to_h
|
|
115
116
|
end
|
|
117
|
+
end
|
|
116
118
|
|
|
119
|
+
def set_command_data
|
|
120
|
+
return unless @attribute.has_key?('opaque_data')
|
|
121
|
+
|
|
122
|
+
@data = @attribute['opaque_data']
|
|
123
|
+
@attribute.delete 'opaque_data'
|
|
124
|
+
end
|
|
117
125
|
end
|
|
118
126
|
end
|
|
119
127
|
end
|
|
@@ -5,29 +5,28 @@ require 'net/https' # this will also include net/http and uri
|
|
|
5
5
|
module Asterisk
|
|
6
6
|
module AJAM
|
|
7
7
|
#
|
|
8
|
-
# == Session class for Asterisk AJAM
|
|
9
|
-
# Used to
|
|
8
|
+
# == Session class for Asterisk AJAM
|
|
9
|
+
# Used to control connection to Asterisk AJAM via HTTP protocol
|
|
10
10
|
#
|
|
11
|
-
|
|
11
|
+
|
|
12
12
|
# Session errors
|
|
13
13
|
# This class extends StandardError and raises when problem with
|
|
14
14
|
# AJAM URL found, for ex: missing scheme or hostname
|
|
15
15
|
# TODO: should be also used for HTTPS connections
|
|
16
|
-
class InvalidURI < StandardError; end
|
|
16
|
+
class InvalidURI < StandardError; end # :nodoc:
|
|
17
17
|
|
|
18
18
|
# This class extends StandardError and raises when problems
|
|
19
|
-
# with
|
|
19
|
+
# with logging AJAM server found or when missing important
|
|
20
20
|
# parameters like username or password.
|
|
21
|
-
class InvalidAMILogin < StandardError; end
|
|
21
|
+
class InvalidAMILogin < StandardError; end # :nodoc:
|
|
22
22
|
|
|
23
23
|
# Class extends StandardError and is raised when trying to
|
|
24
24
|
# send command to not AJAM server where not logged in
|
|
25
|
-
class NotLoggedIn < StandardError; end
|
|
25
|
+
class NotLoggedIn < StandardError; end # :nodoc:
|
|
26
26
|
|
|
27
27
|
# This class establish connection to AJAM server using TCP connection
|
|
28
|
-
# and HTTP protocol.
|
|
28
|
+
# and HTTP protocol.
|
|
29
29
|
class Session
|
|
30
|
-
|
|
31
30
|
# Asterisk AJAM server URI
|
|
32
31
|
# URI class instance
|
|
33
32
|
attr_accessor :uri
|
|
@@ -44,9 +43,9 @@ module Asterisk
|
|
|
44
43
|
# http access password
|
|
45
44
|
attr_accessor :proxy_pass
|
|
46
45
|
|
|
47
|
-
# Create new Asterisk AJAM session without initializing
|
|
46
|
+
# Create new Asterisk AJAM session without initializing
|
|
48
47
|
# TCP network connection
|
|
49
|
-
def initialize(options={})
|
|
48
|
+
def initialize(options = {})
|
|
50
49
|
self.uri = options[:uri]
|
|
51
50
|
@ami_user = options[:ami_user]
|
|
52
51
|
@ami_password = options[:ami_password]
|
|
@@ -55,7 +54,7 @@ module Asterisk
|
|
|
55
54
|
@use_ssl = options[:use_ssl]
|
|
56
55
|
end
|
|
57
56
|
|
|
58
|
-
# login action. Also stores session
|
|
57
|
+
# login action. Also stores session identifier for
|
|
59
58
|
# sending many actions within same session
|
|
60
59
|
def login
|
|
61
60
|
ami_user_valid?
|
|
@@ -69,90 +68,101 @@ module Asterisk
|
|
|
69
68
|
action_command command: command
|
|
70
69
|
end
|
|
71
70
|
|
|
72
|
-
# Verify if session
|
|
71
|
+
# Verify if session established connection and set session id
|
|
73
72
|
def connected?
|
|
74
73
|
/^[0-9a-z]{8}$/.match(@response.session_id).is_a? MatchData
|
|
75
74
|
end
|
|
76
75
|
|
|
77
76
|
private
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
77
|
+
|
|
78
|
+
# handling action_ methods
|
|
79
|
+
def method_missing(method, *args)
|
|
80
|
+
method = method.id2name
|
|
81
|
+
unless /^action_\w+$/.match(method)
|
|
81
82
|
raise NoMethodError,
|
|
82
|
-
|
|
83
|
-
raise NotLoggedIn, "Not logged in" unless connected?
|
|
84
|
-
send_action method.sub(/^action_/,'').to_sym, *args
|
|
83
|
+
"Undefined method #{method}"
|
|
85
84
|
end
|
|
85
|
+
raise NotLoggedIn, 'Not logged in' unless connected?
|
|
86
86
|
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
set_params Hash[action: action].merge params
|
|
90
|
-
@response = http_send_action
|
|
91
|
-
end
|
|
87
|
+
send_action method.sub(/^action_/, '').to_sym, *args
|
|
88
|
+
end
|
|
92
89
|
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
end
|
|
90
|
+
# send action to Asterisk AJAM server
|
|
91
|
+
def send_action(action, params = {})
|
|
92
|
+
set_params Hash[action: action].merge params
|
|
93
|
+
@response = http_send_action
|
|
94
|
+
end
|
|
99
95
|
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
end
|
|
107
|
-
http
|
|
108
|
-
end
|
|
96
|
+
# Send HTTP request to AJAM server using "#uri"
|
|
97
|
+
def http_send_action
|
|
98
|
+
http = http_inst
|
|
99
|
+
req = http_post
|
|
100
|
+
Response.new http.request req
|
|
101
|
+
end
|
|
109
102
|
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
103
|
+
# create new Net::HTTP instance
|
|
104
|
+
def http_inst
|
|
105
|
+
http = Net::HTTP.new(@uri.host, @uri.port)
|
|
106
|
+
if @uri.scheme.downcase.eql? 'https'
|
|
107
|
+
http.use_ssl = true
|
|
108
|
+
http.verify_mode = OpenSSL::SSL::VERIFY_NONE
|
|
116
109
|
end
|
|
110
|
+
http
|
|
111
|
+
end
|
|
117
112
|
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
113
|
+
# create new Net::HTTP::Post instance
|
|
114
|
+
def http_post
|
|
115
|
+
req = Net::HTTP::Post.new @uri.request_uri, request_headers
|
|
116
|
+
req.set_form_data params
|
|
117
|
+
req.basic_auth @proxy_user, @proxy_pass if @proxy_pass && @proxy_user
|
|
118
|
+
req
|
|
119
|
+
end
|
|
122
120
|
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
@params = params
|
|
126
|
-
end
|
|
121
|
+
# Post parameters
|
|
122
|
+
attr_reader :params
|
|
127
123
|
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
end
|
|
124
|
+
# set AJAM POST parameters
|
|
125
|
+
def set_params(params)
|
|
126
|
+
@params = params
|
|
127
|
+
end
|
|
133
128
|
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
"Missing AMI user pass" if @ami_password.to_s.empty?
|
|
138
|
-
end
|
|
129
|
+
# verifies if AMI username is set and not empty
|
|
130
|
+
def ami_user_valid?
|
|
131
|
+
return unless @ami_user.to_s.empty?
|
|
139
132
|
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
133
|
+
raise InvalidAMILogin,
|
|
134
|
+
'Missing AMI username'
|
|
135
|
+
end
|
|
136
|
+
|
|
137
|
+
# verifies if AMI password (secret) is set and not empty
|
|
138
|
+
def ami_pass_valid?
|
|
139
|
+
return unless @ami_password.to_s.empty?
|
|
140
|
+
|
|
141
|
+
raise InvalidAMILogin,
|
|
142
|
+
'Missing AMI user pass'
|
|
143
|
+
end
|
|
144
|
+
|
|
145
|
+
# setup AJAM URI
|
|
146
|
+
def uri=(uri)
|
|
147
|
+
@uri = URI.parse uri
|
|
148
|
+
unless @uri
|
|
145
149
|
raise InvalidURI,
|
|
146
|
-
|
|
150
|
+
'No AJAM URI given'
|
|
147
151
|
end
|
|
152
|
+
return if %w[http https].include? @uri.scheme
|
|
148
153
|
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
154
|
+
raise InvalidURI,
|
|
155
|
+
'Unsupported uri.scheme'
|
|
156
|
+
end
|
|
157
|
+
|
|
158
|
+
# initialize request headers for Net::HTTPRequest class
|
|
159
|
+
def request_headers
|
|
160
|
+
return nil unless @response
|
|
161
|
+
|
|
162
|
+
Hash[
|
|
163
|
+
'Cookie' => %(mansession_id="#{@response.session_id}")
|
|
164
|
+
]
|
|
165
|
+
end
|
|
156
166
|
end
|
|
157
167
|
end
|
|
158
168
|
end
|
data/lib/asterisk/ajam.rb
CHANGED
|
@@ -6,96 +6,96 @@ module Asterisk
|
|
|
6
6
|
# stub successful login
|
|
7
7
|
let(:res_login_ok) do
|
|
8
8
|
http = get_simple_httpok
|
|
9
|
-
http.
|
|
9
|
+
allow(http).to receive(:body).and_return(get_body_success_login)
|
|
10
10
|
http
|
|
11
11
|
end
|
|
12
12
|
# stub failed login
|
|
13
13
|
let(:res_login_fail) do
|
|
14
14
|
http = get_simple_httpok
|
|
15
|
-
http.
|
|
15
|
+
allow(http).to receive(:body).and_return(get_body_failed_login)
|
|
16
16
|
http
|
|
17
17
|
end
|
|
18
18
|
|
|
19
|
-
#new
|
|
20
|
-
describe
|
|
21
|
-
it
|
|
22
|
-
expect
|
|
19
|
+
# new
|
|
20
|
+
describe '#new' do
|
|
21
|
+
it 'raises error if parameter is not Net::HTTPResponse instance' do
|
|
22
|
+
expect do
|
|
23
23
|
Response.new nil
|
|
24
|
-
|
|
24
|
+
end.to raise_error(ArgumentError)
|
|
25
25
|
end
|
|
26
26
|
|
|
27
|
-
it
|
|
27
|
+
it 'raises error if empty response body' do
|
|
28
28
|
http = get_simple_httpok
|
|
29
|
-
http.
|
|
30
|
-
expect
|
|
29
|
+
allow(http).to receive(:body).and_return('')
|
|
30
|
+
expect do
|
|
31
31
|
Response.new http
|
|
32
|
-
|
|
32
|
+
end.to raise_error(InvalidHTTPBody)
|
|
33
33
|
end
|
|
34
34
|
|
|
35
|
-
it
|
|
35
|
+
it 'raises error if invalid xml response body' do
|
|
36
36
|
http = get_simple_httpok
|
|
37
|
-
http.
|
|
38
|
-
expect
|
|
37
|
+
allow(http).to receive(:body).and_return('<a>this is not xml')
|
|
38
|
+
expect do
|
|
39
39
|
Response.new http
|
|
40
|
-
|
|
40
|
+
end.to raise_error(LibXML::XML::Error)
|
|
41
41
|
end
|
|
42
42
|
|
|
43
|
-
it
|
|
44
|
-
sid =
|
|
43
|
+
it 'set session id' do
|
|
44
|
+
sid = 'df901b5f'
|
|
45
45
|
res = res_login_ok
|
|
46
|
-
res.
|
|
46
|
+
allow(res).to receive(:[]).with('Set-Cookie').and_return(%(mansession_id="#{sid}"; Version=1; Max-Age=60))
|
|
47
47
|
response = Response.new res
|
|
48
|
-
response.session_id.
|
|
48
|
+
expect(response.session_id).to eql(sid)
|
|
49
49
|
end
|
|
50
50
|
end
|
|
51
51
|
|
|
52
|
-
#httpok?
|
|
53
|
-
describe
|
|
54
|
-
it
|
|
52
|
+
# httpok?
|
|
53
|
+
describe '#httpok?' do
|
|
54
|
+
it 'returns true if successfully received response from AJAM' do
|
|
55
55
|
res = Response.new res_login_ok
|
|
56
|
-
res.
|
|
56
|
+
expect(res).to be_httpok
|
|
57
57
|
end
|
|
58
|
-
it
|
|
58
|
+
it 'returns false if response from AJAM anything but 200 OK' do
|
|
59
59
|
res = Response.new get_http_unauth
|
|
60
|
-
res.
|
|
60
|
+
expect(res).not_to be_httpok
|
|
61
61
|
end
|
|
62
62
|
end
|
|
63
63
|
|
|
64
|
-
#success?
|
|
65
|
-
describe
|
|
66
|
-
it
|
|
64
|
+
# success?
|
|
65
|
+
describe '#success?' do
|
|
66
|
+
it 'returns true if AJAM response is successful' do
|
|
67
67
|
res = Response.new res_login_ok
|
|
68
|
-
res.
|
|
68
|
+
expect(res).to be_success
|
|
69
69
|
end
|
|
70
|
-
it
|
|
70
|
+
it 'returns false if AJAM response id error' do
|
|
71
71
|
res = Response.new res_login_fail
|
|
72
|
-
res.
|
|
72
|
+
expect(res).not_to be_success
|
|
73
73
|
end
|
|
74
74
|
end
|
|
75
75
|
|
|
76
|
-
describe
|
|
77
|
-
it
|
|
76
|
+
describe 'actions' do
|
|
77
|
+
it 'set received response list to class attributes' do
|
|
78
78
|
http = get_simple_httpok
|
|
79
|
-
http.
|
|
79
|
+
allow(http).to receive(:body).and_return(get_body_sippeers)
|
|
80
80
|
res = Response.new http
|
|
81
|
-
res.list.first.
|
|
82
|
-
res.list.last.
|
|
81
|
+
expect(res.list.first).to include('event' => 'PeerEntry', 'objectname' => '5555')
|
|
82
|
+
expect(res.list.last).to include('objectname' => '8903')
|
|
83
83
|
end
|
|
84
84
|
|
|
85
|
-
it
|
|
85
|
+
it 'when response has single result then it stored in :response' do
|
|
86
86
|
http = get_simple_httpok
|
|
87
|
-
http.
|
|
87
|
+
allow(http).to receive(:body).and_return(get_body_corestatus)
|
|
88
88
|
res = Response.new http
|
|
89
|
-
res.attribute['corestartuptime'].
|
|
89
|
+
expect(res.attribute['corestartuptime']).to eql('17:13:00')
|
|
90
90
|
end
|
|
91
91
|
end
|
|
92
92
|
|
|
93
|
-
describe
|
|
94
|
-
it
|
|
93
|
+
describe '#command' do
|
|
94
|
+
it 'set data variable with response from AJAM' do
|
|
95
95
|
http = get_simple_httpok
|
|
96
|
-
http.
|
|
96
|
+
allow(http).to receive(:body).and_return(cmd_body_dialplan_reload)
|
|
97
97
|
res = Response.new http
|
|
98
|
-
res.data.
|
|
98
|
+
expect(res.data).to match(/Dialplan reloaded/)
|
|
99
99
|
end
|
|
100
100
|
end
|
|
101
101
|
end
|
|
@@ -3,107 +3,104 @@ require 'spec_helper'
|
|
|
3
3
|
module Asterisk
|
|
4
4
|
module AJAM
|
|
5
5
|
describe Session do
|
|
6
|
-
let(:uri_http){'http://ajam.asterisk.com:8088/mxml'}
|
|
7
|
-
let(:manses_id){
|
|
6
|
+
let(:uri_http) { 'http://ajam.asterisk.com:8088/mxml' }
|
|
7
|
+
let(:manses_id) { '84d22b60' }
|
|
8
8
|
let(:http) do
|
|
9
9
|
http = get_simple_httpok
|
|
10
|
-
http.
|
|
11
|
-
http.
|
|
10
|
+
allow(http).to receive(:body).and_return(get_body_success_login)
|
|
11
|
+
allow(http).to receive(:[]).with('Set-Cookie').and_return(%(mansession_id="#{manses_id}"; Version=1; Max-Age=60))
|
|
12
12
|
http
|
|
13
13
|
end
|
|
14
|
-
subject{Session.new uri: uri_http, ami_user: 'admin', ami_password: 'passw0rd'}
|
|
15
|
-
before(:each, :
|
|
16
|
-
Net::HTTP.
|
|
14
|
+
subject { Session.new uri: uri_http, ami_user: 'admin', ami_password: 'passw0rd' }
|
|
15
|
+
before(:each, logginok: true) do
|
|
16
|
+
allow(Net::HTTP).to receive(:new).and_return(double('Net::HTTP', :request => http, :use_ssl= => true))
|
|
17
17
|
end
|
|
18
18
|
|
|
19
|
-
describe
|
|
20
|
-
it
|
|
21
|
-
URI.
|
|
22
|
-
URI.should_receive(:parse).with(uri_http)
|
|
19
|
+
describe '#new' do
|
|
20
|
+
it 'set AJAM uri' do
|
|
21
|
+
expect(URI).to receive(:parse).with(uri_http).and_return(URI(uri_http))
|
|
23
22
|
Session.new uri: uri_http
|
|
24
23
|
end
|
|
25
24
|
|
|
26
|
-
it
|
|
27
|
-
expect
|
|
25
|
+
it 'raises error if URI missing ' do
|
|
26
|
+
expect do
|
|
28
27
|
Session.new
|
|
29
|
-
|
|
28
|
+
end.to raise_error(URI::InvalidURIError)
|
|
30
29
|
end
|
|
31
30
|
|
|
32
|
-
it
|
|
33
|
-
uri =
|
|
34
|
-
expect
|
|
31
|
+
it 'raises error if scheme is not http or https' do
|
|
32
|
+
uri = 'ftp://127.0.0.1/path'
|
|
33
|
+
expect do
|
|
35
34
|
Session.new uri: uri
|
|
36
|
-
|
|
35
|
+
end.to raise_error(InvalidURI)
|
|
37
36
|
end
|
|
38
37
|
end
|
|
39
38
|
|
|
40
|
-
describe
|
|
41
|
-
it
|
|
39
|
+
describe '#login', logginok: true do
|
|
40
|
+
it 'raises InvalidAMILogin if empty AMI username' do
|
|
42
41
|
subject.ami_user = nil
|
|
43
|
-
expect
|
|
42
|
+
expect do
|
|
44
43
|
subject.login
|
|
45
|
-
|
|
44
|
+
end.to raise_error(InvalidAMILogin)
|
|
46
45
|
end
|
|
47
46
|
|
|
48
|
-
it
|
|
47
|
+
it 'raises InvalidAMILogin if empty AMI password' do
|
|
49
48
|
subject.ami_password = nil
|
|
50
|
-
expect
|
|
49
|
+
expect do
|
|
51
50
|
subject.login
|
|
52
|
-
|
|
51
|
+
end.to raise_error(InvalidAMILogin)
|
|
53
52
|
end
|
|
54
53
|
|
|
55
|
-
it
|
|
54
|
+
it 'should return Session class instance on success', mock_login: true do
|
|
56
55
|
response = subject.login
|
|
57
|
-
response.
|
|
56
|
+
expect(response).to be_kind_of(Session)
|
|
58
57
|
end
|
|
59
|
-
|
|
60
58
|
end
|
|
61
59
|
|
|
62
|
-
describe
|
|
63
|
-
it
|
|
60
|
+
describe '#connected?', logginok: true do
|
|
61
|
+
it 'returns true when successfully logged in', mock_login: true do
|
|
64
62
|
subject.login
|
|
65
|
-
subject.
|
|
63
|
+
expect(subject).to be_connected
|
|
66
64
|
end
|
|
67
65
|
end
|
|
68
66
|
|
|
69
|
-
describe
|
|
70
|
-
before(:each) {subject.
|
|
71
|
-
it
|
|
72
|
-
subject.
|
|
67
|
+
describe 'action methods' do
|
|
68
|
+
before(:each) { allow(subject).to receive(:connected?).and_return(true) }
|
|
69
|
+
it 'when call action without parameters then expects send_action with action name' do
|
|
70
|
+
expect(subject).to receive(:send_action).once.with(:sippeers)
|
|
73
71
|
subject.action_sippeers
|
|
74
72
|
end
|
|
75
73
|
|
|
76
|
-
it
|
|
74
|
+
it 'when call action with parameters then expects send_action with hash' do
|
|
77
75
|
params = Hash[family: 'user', key: 'extension', val: '5555']
|
|
78
|
-
subject.
|
|
76
|
+
expect(subject).to receive(:send_action).with(:dbput, params)
|
|
79
77
|
subject.action_dbput params
|
|
80
78
|
end
|
|
81
79
|
|
|
82
|
-
it
|
|
80
|
+
it 'uses cookies when sending action', logginok: true do
|
|
83
81
|
subject.login
|
|
84
|
-
Net::HTTP::Post.
|
|
85
|
-
with(anything
|
|
86
|
-
and_return(double('Net::HTTPResponse', :
|
|
82
|
+
expect(Net::HTTP::Post).to receive(:new)
|
|
83
|
+
.with(anything, hash_including('Cookie' => %(mansession_id="#{manses_id}")))
|
|
84
|
+
.and_return(double('Net::HTTPResponse', set_form_data: true))
|
|
87
85
|
subject.action_corestatus
|
|
88
86
|
end
|
|
89
87
|
end
|
|
90
88
|
|
|
91
|
-
describe
|
|
92
|
-
before(:each) {subject.
|
|
93
|
-
it
|
|
89
|
+
describe '#command' do
|
|
90
|
+
before(:each) { allow(subject).to receive(:connected?).and_return(true) }
|
|
91
|
+
it 'sends command to action method' do
|
|
94
92
|
http = get_simple_httpok
|
|
95
|
-
http.
|
|
96
|
-
http.
|
|
97
|
-
http.
|
|
98
|
-
http.
|
|
99
|
-
http.
|
|
100
|
-
Net::HTTP.
|
|
101
|
-
Net::HTTP::Post.
|
|
93
|
+
allow(http).to receive(:body).and_return(cmd_body_sip_show_peers)
|
|
94
|
+
allow(http).to receive(:[]).and_return(%(mansession_id="#{manses_id}"; Version=1; Max-Age=60))
|
|
95
|
+
allow(http).to receive(:set_form_data).with(anything)
|
|
96
|
+
allow(http).to receive(:use_ssl=)
|
|
97
|
+
allow(http).to receive(:request).and_return(http)
|
|
98
|
+
allow(Net::HTTP).to receive(:new).and_return(http)
|
|
99
|
+
allow(Net::HTTP::Post).to receive(:new).and_return(double('Net::HTTP::Post', set_form_data: true))
|
|
102
100
|
res = subject.command 'sip show peers'
|
|
103
|
-
res.data.
|
|
101
|
+
expect(res.data).to match(/88888\s+\(Unspecified\)/)
|
|
104
102
|
end
|
|
105
103
|
end
|
|
106
|
-
|
|
107
104
|
end
|
|
108
105
|
end
|
|
109
106
|
end
|
data/spec/spec_helper.rb
CHANGED
|
@@ -1,16 +1,16 @@
|
|
|
1
1
|
require 'simplecov'
|
|
2
2
|
require 'coveralls'
|
|
3
|
-
|
|
4
|
-
SimpleCov.formatter = SimpleCov::Formatter::MultiFormatter[
|
|
3
|
+
|
|
4
|
+
SimpleCov.formatter = SimpleCov::Formatter::MultiFormatter.new([
|
|
5
5
|
SimpleCov::Formatter::HTMLFormatter,
|
|
6
6
|
Coveralls::SimpleCov::Formatter
|
|
7
|
-
]
|
|
7
|
+
])
|
|
8
8
|
SimpleCov.start do
|
|
9
|
-
add_filter
|
|
9
|
+
add_filter 'spec/'
|
|
10
10
|
end
|
|
11
11
|
require 'asterisk/ajam'
|
|
12
12
|
|
|
13
|
-
# this fixture create valid HTTPOK response
|
|
13
|
+
# this fixture create valid HTTPOK response
|
|
14
14
|
def get_simple_httpok
|
|
15
15
|
Net::HTTPOK.new 1.1, 200, 'OK'
|
|
16
16
|
end
|
|
@@ -20,22 +20,22 @@ def get_http_unauth
|
|
|
20
20
|
end
|
|
21
21
|
|
|
22
22
|
def get_body_success_login
|
|
23
|
-
|
|
23
|
+
"<ajax-response>
|
|
24
24
|
<response type='object' id='unknown'><generic response='Success' message='Authentication accepted' /></response>
|
|
25
25
|
</ajax-response>
|
|
26
|
-
|
|
26
|
+
"
|
|
27
27
|
end
|
|
28
28
|
|
|
29
29
|
def get_body_failed_login
|
|
30
|
-
|
|
30
|
+
"
|
|
31
31
|
<ajax-response>
|
|
32
32
|
<response type='object' id='unknown'><generic response='Error' message='Authentication failed' /></response>
|
|
33
33
|
</ajax-response>
|
|
34
|
-
|
|
34
|
+
"
|
|
35
35
|
end
|
|
36
36
|
|
|
37
37
|
def get_body_sippeers
|
|
38
|
-
|
|
38
|
+
"<ajax-response>
|
|
39
39
|
<response type='object' id='unknown'><generic response='Success' actionid='321321321' eventlist='start' message='Peer status list will follow' /></response>
|
|
40
40
|
<response type='object' id='unknown'><generic event='PeerEntry' actionid='321321321' channeltype='SIP' objectname='5555' chanobjecttype='peer' ipaddress='-none-' ipport='0' dynamic='yes' forcerport='no' videosupport='no' textsupport='no' acl='yes' status='UNKNOWN' realtimedevice='no' /></response>
|
|
41
41
|
<response type='object' id='unknown'><generic event='PeerEntry' actionid='321321321' channeltype='SIP' objectname='8734' chanobjecttype='peer' ipaddress='-none-' ipport='0' dynamic='yes' forcerport='yes' videosupport='no' textsupport='no' acl='yes' status='UNKNOWN' realtimedevice='no' /></response>
|
|
@@ -47,26 +47,26 @@ def get_body_sippeers
|
|
|
47
47
|
<response type='object' id='unknown'><generic event='PeerEntry' actionid='321321321' channeltype='SIP' objectname='8903' chanobjecttype='peer' ipaddress='-none-' ipport='0' dynamic='yes' forcerport='yes' videosupport='no' textsupport='no' acl='yes' status='UNKNOWN' realtimedevice='no' /></response>
|
|
48
48
|
<response type='object' id='unknown'><generic event='PeerlistComplete' eventlist='Complete' listitems='8' actionid='321321321' /></response>
|
|
49
49
|
</ajax-response>
|
|
50
|
-
|
|
50
|
+
"
|
|
51
51
|
end
|
|
52
52
|
|
|
53
53
|
def get_body_corestatus
|
|
54
|
-
|
|
54
|
+
"<ajax-response>
|
|
55
55
|
<response type='object' id='unknown'><generic response='Success' corestartupdate='2013-05-25' corestartuptime='17:13:00' corereloaddate='2013-05-25' corereloadtime='17:13:00' corecurrentcalls='0' /></response>
|
|
56
56
|
</ajax-response>
|
|
57
|
-
|
|
57
|
+
"
|
|
58
58
|
end
|
|
59
59
|
|
|
60
60
|
def cmd_body_dialplan_reload
|
|
61
|
-
|
|
61
|
+
"<ajax-response>
|
|
62
62
|
<response type='object' id='unknown'><generic response='Follows' privilege='Command' opaque_data='Dialplan reloaded.
|
|
63
63
|
--END COMMAND--' /></response>
|
|
64
64
|
</ajax-response>
|
|
65
|
-
|
|
65
|
+
"
|
|
66
66
|
end
|
|
67
67
|
|
|
68
68
|
def cmd_body_sip_show_peers
|
|
69
|
-
|
|
69
|
+
"<ajax-response>
|
|
70
70
|
<response type='object' id='unknown'><generic response='Follows' privilege='Command' opaque_data='Name/username Host Dyn Forcerport ACL Port Status
|
|
71
71
|
5555 (Unspecified) D A 0 UNKNOWN
|
|
72
72
|
8734 (Unspecified) D N A 0 UNKNOWN
|
|
@@ -79,5 +79,5 @@ def cmd_body_sip_show_peers
|
|
|
79
79
|
8 sip peers [Monitored: 0 online, 8 offline Unmonitored: 0 online, 0 offline]
|
|
80
80
|
--END COMMAND--' /></response>
|
|
81
81
|
</ajax-response>
|
|
82
|
-
|
|
82
|
+
"
|
|
83
83
|
end
|
metadata
CHANGED
|
@@ -1,95 +1,95 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: asterisk-ajam
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.0
|
|
4
|
+
version: 0.1.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Stas Kobzar
|
|
8
|
-
autorequire:
|
|
9
8
|
bindir: bin
|
|
10
9
|
cert_chain: []
|
|
11
|
-
date:
|
|
10
|
+
date: 1980-01-02 00:00:00.000000000 Z
|
|
12
11
|
dependencies:
|
|
13
12
|
- !ruby/object:Gem::Dependency
|
|
14
13
|
name: bundler
|
|
15
14
|
requirement: !ruby/object:Gem::Requirement
|
|
16
15
|
requirements:
|
|
17
|
-
- -
|
|
16
|
+
- - ">="
|
|
18
17
|
- !ruby/object:Gem::Version
|
|
19
|
-
version: '
|
|
18
|
+
version: '0'
|
|
20
19
|
type: :development
|
|
21
20
|
prerelease: false
|
|
22
21
|
version_requirements: !ruby/object:Gem::Requirement
|
|
23
22
|
requirements:
|
|
24
|
-
- -
|
|
23
|
+
- - ">="
|
|
25
24
|
- !ruby/object:Gem::Version
|
|
26
|
-
version: '
|
|
25
|
+
version: '0'
|
|
27
26
|
- !ruby/object:Gem::Dependency
|
|
28
27
|
name: rake
|
|
29
28
|
requirement: !ruby/object:Gem::Requirement
|
|
30
29
|
requirements:
|
|
31
|
-
- -
|
|
30
|
+
- - ">="
|
|
32
31
|
- !ruby/object:Gem::Version
|
|
33
32
|
version: '0'
|
|
34
33
|
type: :development
|
|
35
34
|
prerelease: false
|
|
36
35
|
version_requirements: !ruby/object:Gem::Requirement
|
|
37
36
|
requirements:
|
|
38
|
-
- -
|
|
37
|
+
- - ">="
|
|
39
38
|
- !ruby/object:Gem::Version
|
|
40
39
|
version: '0'
|
|
41
40
|
- !ruby/object:Gem::Dependency
|
|
42
41
|
name: rdoc
|
|
43
42
|
requirement: !ruby/object:Gem::Requirement
|
|
44
43
|
requirements:
|
|
45
|
-
- -
|
|
44
|
+
- - ">="
|
|
46
45
|
- !ruby/object:Gem::Version
|
|
47
46
|
version: '0'
|
|
48
47
|
type: :development
|
|
49
48
|
prerelease: false
|
|
50
49
|
version_requirements: !ruby/object:Gem::Requirement
|
|
51
50
|
requirements:
|
|
52
|
-
- -
|
|
51
|
+
- - ">="
|
|
53
52
|
- !ruby/object:Gem::Version
|
|
54
53
|
version: '0'
|
|
55
54
|
- !ruby/object:Gem::Dependency
|
|
56
55
|
name: rspec
|
|
57
56
|
requirement: !ruby/object:Gem::Requirement
|
|
58
57
|
requirements:
|
|
59
|
-
- -
|
|
58
|
+
- - ">="
|
|
60
59
|
- !ruby/object:Gem::Version
|
|
61
60
|
version: '0'
|
|
62
61
|
type: :development
|
|
63
62
|
prerelease: false
|
|
64
63
|
version_requirements: !ruby/object:Gem::Requirement
|
|
65
64
|
requirements:
|
|
66
|
-
- -
|
|
65
|
+
- - ">="
|
|
67
66
|
- !ruby/object:Gem::Version
|
|
68
67
|
version: '0'
|
|
69
68
|
- !ruby/object:Gem::Dependency
|
|
70
69
|
name: libxml-ruby
|
|
71
70
|
requirement: !ruby/object:Gem::Requirement
|
|
72
71
|
requirements:
|
|
73
|
-
- -
|
|
72
|
+
- - ">="
|
|
74
73
|
- !ruby/object:Gem::Version
|
|
75
74
|
version: '0'
|
|
76
75
|
type: :runtime
|
|
77
76
|
prerelease: false
|
|
78
77
|
version_requirements: !ruby/object:Gem::Requirement
|
|
79
78
|
requirements:
|
|
80
|
-
- -
|
|
79
|
+
- - ">="
|
|
81
80
|
- !ruby/object:Gem::Version
|
|
82
81
|
version: '0'
|
|
83
82
|
description: Ruby module for interacting with Asterisk management interface (AMI)
|
|
84
83
|
via HTTP
|
|
85
84
|
email:
|
|
86
|
-
-
|
|
85
|
+
- staskobzar@gmail.com
|
|
87
86
|
executables: []
|
|
88
87
|
extensions: []
|
|
89
88
|
extra_rdoc_files: []
|
|
90
89
|
files:
|
|
91
|
-
- .
|
|
92
|
-
- .
|
|
90
|
+
- ".github/copilot-instructions.md"
|
|
91
|
+
- ".gitignore"
|
|
92
|
+
- ".travis.yml"
|
|
93
93
|
- Gemfile
|
|
94
94
|
- LICENSE.txt
|
|
95
95
|
- README.md
|
|
@@ -107,24 +107,21 @@ homepage: https://github.com/staskobzar/asterisk-ajam
|
|
|
107
107
|
licenses:
|
|
108
108
|
- MIT
|
|
109
109
|
metadata: {}
|
|
110
|
-
post_install_message:
|
|
111
110
|
rdoc_options: []
|
|
112
111
|
require_paths:
|
|
113
112
|
- lib
|
|
114
113
|
required_ruby_version: !ruby/object:Gem::Requirement
|
|
115
114
|
requirements:
|
|
116
|
-
- -
|
|
115
|
+
- - ">="
|
|
117
116
|
- !ruby/object:Gem::Version
|
|
118
|
-
version:
|
|
117
|
+
version: 3.2.0
|
|
119
118
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
120
119
|
requirements:
|
|
121
|
-
- -
|
|
120
|
+
- - ">="
|
|
122
121
|
- !ruby/object:Gem::Version
|
|
123
122
|
version: '0'
|
|
124
123
|
requirements: []
|
|
125
|
-
|
|
126
|
-
rubygems_version: 2.0.3
|
|
127
|
-
signing_key:
|
|
124
|
+
rubygems_version: 4.0.6
|
|
128
125
|
specification_version: 4
|
|
129
126
|
summary: AMI via HTTP
|
|
130
127
|
test_files:
|