asterisk-ari-client 0.0.1

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 (100) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +14 -0
  3. data/Gemfile +4 -0
  4. data/LICENSE.txt +22 -0
  5. data/README.md +85 -0
  6. data/Rakefile +54 -0
  7. data/asterisk-ari-client.gemspec +32 -0
  8. data/examples/list_channels.rb +14 -0
  9. data/examples/websocket.rb +33 -0
  10. data/lib/ari.rb +12 -0
  11. data/lib/ari/client.rb +124 -0
  12. data/lib/ari/generators/api.rb +23 -0
  13. data/lib/ari/generators/attribute.rb +35 -0
  14. data/lib/ari/generators/model.rb +38 -0
  15. data/lib/ari/generators/operation.rb +43 -0
  16. data/lib/ari/generators/parameter.rb +35 -0
  17. data/lib/ari/generators/property.rb +32 -0
  18. data/lib/ari/generators/resource_generator.rb +94 -0
  19. data/lib/ari/generators/templates/model.rb.erb +36 -0
  20. data/lib/ari/generators/templates/resource.rb.erb +97 -0
  21. data/lib/ari/list_resource.rb +23 -0
  22. data/lib/ari/model.rb +26 -0
  23. data/lib/ari/models.rb +47 -0
  24. data/lib/ari/models/.DS_Store +0 -0
  25. data/lib/ari/models/application_replaced.rb +17 -0
  26. data/lib/ari/models/asterisk_info.rb +35 -0
  27. data/lib/ari/models/bridge_attended_transfer.rb +63 -0
  28. data/lib/ari/models/bridge_blind_transfer.rb +35 -0
  29. data/lib/ari/models/bridge_created.rb +23 -0
  30. data/lib/ari/models/bridge_destroyed.rb +23 -0
  31. data/lib/ari/models/bridge_merged.rb +27 -0
  32. data/lib/ari/models/build_info.rb +19 -0
  33. data/lib/ari/models/caller_id.rb +19 -0
  34. data/lib/ari/models/channel_caller_id.rb +23 -0
  35. data/lib/ari/models/channel_created.rb +23 -0
  36. data/lib/ari/models/channel_destroyed.rb +23 -0
  37. data/lib/ari/models/channel_dialplan.rb +23 -0
  38. data/lib/ari/models/channel_dtmf_received.rb +23 -0
  39. data/lib/ari/models/channel_entered_bridge.rb +27 -0
  40. data/lib/ari/models/channel_hangup_request.rb +23 -0
  41. data/lib/ari/models/channel_left_bridge.rb +27 -0
  42. data/lib/ari/models/channel_state_change.rb +23 -0
  43. data/lib/ari/models/channel_talking_finished.rb +23 -0
  44. data/lib/ari/models/channel_talking_started.rb +23 -0
  45. data/lib/ari/models/channel_userevent.rb +35 -0
  46. data/lib/ari/models/channel_varset.rb +23 -0
  47. data/lib/ari/models/config_info.rb +27 -0
  48. data/lib/ari/models/device_state_changed.rb +23 -0
  49. data/lib/ari/models/dial.rb +31 -0
  50. data/lib/ari/models/dialed.rb +17 -0
  51. data/lib/ari/models/dialplan_cep.rb +19 -0
  52. data/lib/ari/models/endpoint_state_change.rb +23 -0
  53. data/lib/ari/models/format_lang_pair.rb +19 -0
  54. data/lib/ari/models/live_recording.rb +19 -0
  55. data/lib/ari/models/message.rb +19 -0
  56. data/lib/ari/models/missing_params.rb +19 -0
  57. data/lib/ari/models/playback_finished.rb +23 -0
  58. data/lib/ari/models/playback_started.rb +23 -0
  59. data/lib/ari/models/recording_failed.rb +23 -0
  60. data/lib/ari/models/recording_finished.rb +23 -0
  61. data/lib/ari/models/recording_started.rb +23 -0
  62. data/lib/ari/models/set_id.rb +19 -0
  63. data/lib/ari/models/stasis_end.rb +23 -0
  64. data/lib/ari/models/stasis_start.rb +27 -0
  65. data/lib/ari/models/status_info.rb +27 -0
  66. data/lib/ari/models/stored_recording.rb +19 -0
  67. data/lib/ari/models/system_info.rb +19 -0
  68. data/lib/ari/models/text_message.rb +23 -0
  69. data/lib/ari/models/text_message_received.rb +27 -0
  70. data/lib/ari/models/text_message_variable.rb +19 -0
  71. data/lib/ari/models/variable.rb +19 -0
  72. data/lib/ari/request_error.rb +8 -0
  73. data/lib/ari/resource.rb +37 -0
  74. data/lib/ari/resources.rb +11 -0
  75. data/lib/ari/resources/application.rb +101 -0
  76. data/lib/ari/resources/asterisk.rb +72 -0
  77. data/lib/ari/resources/bridge.rb +286 -0
  78. data/lib/ari/resources/channel.rb +610 -0
  79. data/lib/ari/resources/device_state.rb +98 -0
  80. data/lib/ari/resources/endpoint.rb +124 -0
  81. data/lib/ari/resources/event.rb +25 -0
  82. data/lib/ari/resources/mailbox.rb +99 -0
  83. data/lib/ari/resources/playback.rb +86 -0
  84. data/lib/ari/resources/recording.rb +241 -0
  85. data/lib/ari/resources/sound.rb +64 -0
  86. data/lib/ari/server_error.rb +8 -0
  87. data/lib/asterisk/ari/client.rb +10 -0
  88. data/lib/asterisk/ari/client/version.rb +7 -0
  89. data/test/fixtures/bridge_create.json +74 -0
  90. data/test/fixtures/bridge_get.json +74 -0
  91. data/test/fixtures/bridges_list.json +76 -0
  92. data/test/fixtures/channel_get.json +83 -0
  93. data/test/fixtures/channel_originate.json +83 -0
  94. data/test/fixtures/channel_originate_with_id.json +83 -0
  95. data/test/fixtures/channels_list.json +105 -0
  96. data/test/lib/asterisk-ari-client/.DS_Store +0 -0
  97. data/test/lib/asterisk-ari-client/bridge_test.rb +46 -0
  98. data/test/lib/asterisk-ari-client/channel_test.rb +59 -0
  99. data/test/test_helper.rb +42 -0
  100. metadata +265 -0
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 85b27638324674e15bd2f5220dc966c300ca2784
4
+ data.tar.gz: 5550be4213033e9599c04ec67ffede7c2bfa8ad3
5
+ SHA512:
6
+ metadata.gz: ee5e99c5b46cfe4c15e290a823ab4263d9a0547c980cb14799b792a4e856686464ae5624e1b5676d4619b0c49ab57b786b5b3bc246ae408e55fb387346b994a4
7
+ data.tar.gz: fa4a32b9192197a9f9ec2a1631db0d839b67b0c763775c83e7b68be6a966e32bf8bc3e7884cb791f8d039af5d67f042a823f063b333b3e841c63f837852c1f4f
data/.gitignore ADDED
@@ -0,0 +1,14 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /Gemfile.lock
4
+ /_yardoc/
5
+ /coverage/
6
+ /doc/
7
+ /pkg/
8
+ /spec/reports/
9
+ /tmp/
10
+ *.bundle
11
+ *.so
12
+ *.o
13
+ *.a
14
+ mkmf.log
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in asterisk-ari-client.gemspec
4
+ gemspec
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2014 Jan Svoboda
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,85 @@
1
+ # About
2
+
3
+ This repository contains the ruby client library for the Asterisk REST Interface (ARI).
4
+ It uses the swagger ARI json definitions to generate ruby classes.
5
+
6
+ ## Installation
7
+
8
+ Add this line to your application's Gemfile:
9
+
10
+ ```ruby
11
+ gem 'asterisk-ari-client'
12
+ ```
13
+
14
+ And then execute:
15
+
16
+ $ bundle
17
+
18
+ Or install it yourself as:
19
+
20
+ $ gem install asterisk-ari-client
21
+
22
+ ## Usage
23
+
24
+ ```ruby
25
+ # instantiate client
26
+ @client = Ari::Client.new(
27
+ url: 'http://127.0.0.1:8088/ari',
28
+ api_key: 'asterisk:asterisk',
29
+ app: 'my-app'
30
+ )
31
+
32
+ # originate
33
+ @client.channels.originate endpoint: 'PJSIP/endpoint-name', extension: 11
34
+
35
+ # list all channels
36
+ channels = @client.channels.list
37
+ ```
38
+
39
+ When working with only one client you can also use the following:
40
+
41
+ ```ruby
42
+ Ari.client = Ari::Client.new(
43
+ url: 'http://127.0.0.1:8088/ari',
44
+ api_key: 'asterisk:asterisk',
45
+ app: 'my-app'
46
+ )
47
+
48
+ # list channels
49
+ channels = Ari::Channel.list
50
+ ```
51
+
52
+ ### WebSocket events
53
+
54
+ ```ruby
55
+ # listen to events
56
+ @client.on :websocket_open do
57
+ puts "Connected !"
58
+ end
59
+
60
+ @client.on :stasis_start do |e|
61
+ puts "Received call to #{e.channel.dialplan.exten} !"
62
+
63
+ e.channel.answer
64
+
65
+ e.channel.on :channel_dtmf_received do |e|
66
+ puts "Digit pressed: #{e.digit} on channel #{e.channel.name} !"
67
+ end
68
+
69
+ e.channel.on :stasis_end do |e|
70
+ puts "Channel #{e.channel.name} left Stasis."
71
+ end
72
+ end
73
+
74
+ # start websocket to receive events
75
+ @client.connect_websocket
76
+ sleep
77
+ ```
78
+
79
+ ## Contributing
80
+
81
+ 1. Fork it ( https://github.com/svoboda-jan/asterisk-ari/fork )
82
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
83
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
84
+ 4. Push to the branch (`git push origin my-new-feature`)
85
+ 5. Create a new Pull Request
data/Rakefile ADDED
@@ -0,0 +1,54 @@
1
+ require "bundler/gem_tasks"
2
+ require 'open-uri'
3
+ require 'json'
4
+ require 'ari/generators/resource_generator'
5
+
6
+ desc "Generate resources from JSON specification"
7
+ task :generate do
8
+
9
+ base_url = 'http://svn.asterisk.org/svn/asterisk/trunk/rest-api/api-docs/%{resource_name}.json'
10
+ resources = %w{ applications asterisk bridges channels deviceStates endpoints
11
+ events mailboxes playbacks recordings sounds
12
+ }
13
+
14
+ resource_options = {
15
+ asterisk: {
16
+ resource_klass_name: 'AsteriskInfo'
17
+ },
18
+ applications: {
19
+ id_attribute_name: 'applicationName'
20
+ },
21
+ events: {
22
+ only_models: true
23
+ }
24
+ }
25
+
26
+ models_path = File.join(__dir__, 'lib', 'ari', 'models.rb')
27
+ FileUtils.rm_f models_path
28
+ models_file = File.new(models_path, 'a')
29
+
30
+ resources_path = File.join(__dir__, 'lib', 'ari', 'resources.rb')
31
+ FileUtils.rm_f resources_path
32
+ resources_file = File.new(resources_path, 'a')
33
+
34
+ resources.each do |resource_name|
35
+ url = base_url % { resource_name: resource_name }
36
+ puts ">> generating #{resource_name} from #{url}"
37
+ json = JSON.parse open(url).read
38
+ generator = Ari::Generators::ResourceGenerator.new(
39
+ resource_name,
40
+ json,
41
+ resource_options[resource_name.to_sym] || {}
42
+ )
43
+ generator.generate
44
+
45
+ resources_file.puts "require 'ari/resources/#{generator.resource_name}'"
46
+ generator.models.each do |model|
47
+ next if model.name == generator.resource_name
48
+ models_file.puts "require 'ari/models/#{model.name}'"
49
+ end
50
+ end
51
+
52
+ models_file.close
53
+
54
+ end
@@ -0,0 +1,32 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'asterisk/ari/client/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "asterisk-ari-client"
8
+ spec.version = Asterisk::Ari::Client::VERSION
9
+ spec.authors = ["Jan Svoboda"]
10
+ spec.email = ["jan@mluv.cz"]
11
+ spec.summary = %q{Ruby client library for the Asterisk REST Interface (ARI).}
12
+ spec.description = %q{Ruby client library for the Asterisk REST Interface (ARI).}
13
+ spec.homepage = ""
14
+ spec.license = "MIT"
15
+
16
+ spec.files = `git ls-files -z`.split("\x0")
17
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
+ spec.require_paths = ["lib"]
20
+
21
+ spec.add_development_dependency "bundler", "~> 1.7"
22
+ spec.add_development_dependency "rake", "~> 10.0"
23
+
24
+ spec.add_development_dependency "minitest", "~> 5.4.2"
25
+ spec.add_development_dependency "vcr", "~> 2.9.3"
26
+ spec.add_development_dependency "webmock", "~> 1.19.0"
27
+
28
+ spec.add_development_dependency "active_support", "~> 4.1.6"
29
+
30
+ spec.add_dependency "websocket-client-simple", "~> 0.2.0"
31
+ spec.add_dependency "event_emitter", "~> 0.2.5"
32
+ end
@@ -0,0 +1,14 @@
1
+ require 'asterisk-ari-client'
2
+
3
+ @client = Ari::Client.new(
4
+ url: 'http://192.168.1.23:8088/ari',
5
+ api_key: 'asterisk:asterisk',
6
+ app: 'dialplan'
7
+ )
8
+
9
+ # list channels
10
+ channels = @client.channels.list
11
+
12
+ channels.each do |channel|
13
+ puts "Channel ID #{channel.id}: #{channel.name}"
14
+ end
@@ -0,0 +1,33 @@
1
+ require 'asterisk-ari-client'
2
+
3
+ Ari.client = Ari::Client.new(
4
+ url: 'http://192.168.1.23:8088/ari',
5
+ api_key: 'asterisk:asterisk',
6
+ app: 'dialplan'
7
+ )
8
+
9
+ @client.on :websocket_open do
10
+ puts "Connected !"
11
+ end
12
+
13
+ @client.on :websocket_error do |err|
14
+ puts "Error :( #{err}"
15
+ end
16
+
17
+ @client.on :stasis_start do |e|
18
+ puts "Received call to #{e.channel.dialplan.exten} !"
19
+
20
+ e.channel.answer
21
+
22
+ e.channel.on :channel_dtmf_received do |e|
23
+ puts "Digit pressed: #{e.digit} on channel #{e.channel.name} !"
24
+ end
25
+
26
+ e.channel.on :stasis_end do |e|
27
+ puts "Channel #{e.channel.name} left Stasis."
28
+ end
29
+ end
30
+
31
+ # start websocket to receive events
32
+ @client.connect_websocket
33
+ sleep
data/lib/ari.rb ADDED
@@ -0,0 +1,12 @@
1
+ module Ari
2
+
3
+ def self.client
4
+ return @@client if defined?(@@client)
5
+ raise "ARI client not set. You can set it with ARI.client = client."
6
+ end
7
+
8
+ def self.client=(val)
9
+ @@client = val
10
+ end
11
+
12
+ end
data/lib/ari/client.rb ADDED
@@ -0,0 +1,124 @@
1
+ require 'net/http'
2
+ require 'multi_json'
3
+ require 'ari/request_error'
4
+ require 'ari/server_error'
5
+ require 'event_emitter'
6
+ require 'websocket-client-simple'
7
+
8
+ module Ari
9
+ class Client
10
+ include EventEmitter
11
+
12
+ attr_reader :ws
13
+
14
+ DEFAULTS = {
15
+ :url => 'http://localhost:8088/ari'
16
+ }
17
+
18
+ HTTP_HEADERS = {
19
+ 'Content-Type' => 'json',
20
+ 'Accept' => 'application/json',
21
+ 'Accept-Charset' => 'utf-8',
22
+ 'User-Agent' => "asterisk-ari-client/#{::Asterisk::Ari::Client::VERSION} ruby/#{RUBY_VERSION}"
23
+ }
24
+
25
+ def initialize(options = {})
26
+ @options = DEFAULTS.merge! options
27
+ @uri = URI.parse @options[:url]
28
+ raise ArgumentError.new("The :api_key needs to be specified.") unless @options[:api_key]
29
+ end
30
+
31
+ %w{ get put post delete }.each do |http_method|
32
+ method_klass = Net::HTTP.const_get http_method.to_s.capitalize
33
+ define_method http_method do |path, params = {}|
34
+ params.merge!({ api_key: @options[:api_key], app: @options[:app] })
35
+ query_string = URI.encode_www_form params
36
+ request_path = "#{@uri.path}#{path}?#{query_string}"
37
+ request = method_klass.new request_path, HTTP_HEADERS
38
+ send_request request
39
+ end
40
+ end
41
+
42
+ def connect_websocket
43
+ params = { api_key: @options[:api_key], app: @options[:app] }
44
+ query_string = URI.encode_www_form params
45
+ ws_url = "#{@uri}/events?#{query_string}"
46
+ if @ws
47
+ %w{ open message error close }.each { |e| @ws.remove_listener(e) }
48
+ @ws.close
49
+ end
50
+ @ws = WebSocket::Client::Simple.connect ws_url
51
+ @ws.on :open, &method(:handle_websocket_open)
52
+ @ws.on :message, &method(:handle_websocket_message)
53
+ @ws.on :error, &method(:handle_websocket_error)
54
+ @ws.on :close, &method(:handle_websocket_close)
55
+ end
56
+
57
+ private
58
+
59
+ def handle_websocket_open
60
+ self.emit :websocket_open
61
+ end
62
+
63
+ def handle_websocket_message(event)
64
+ Thread.new do
65
+ begin
66
+ handle_websocket_close(event) and next if event.type == :close
67
+ next unless event.type == :text
68
+
69
+ object = MultiJson.load event.data
70
+
71
+ handler_klass = Ari.const_get object['type'] rescue nil
72
+ next unless handler_klass
73
+ event_name = object['type'].gsub(/([a-z\d])([A-Z])/,'\1_\2').downcase
74
+
75
+ instances = self.class.instance_listeners[event_name.to_sym]
76
+
77
+ event_properties = (handler_klass.instance_methods - Ari::Event.instance_methods)
78
+ event_properties.reject! { |p| p.to_s.end_with? '=' }
79
+ handler = handler_klass.new(object.merge(client: self))
80
+ event_model_ids = event_properties.map { |p| handler.send(p).id rescue nil }.compact
81
+ [*instances].each do |instance|
82
+ if event_model_ids.include? instance.id
83
+ emit "#{event_name}-#{instance.id}", handler
84
+ end
85
+ end
86
+
87
+ self.emit event_name, handler_klass.new(object.merge(client: self))
88
+ rescue => err
89
+ emit :websocket_error, err
90
+ end
91
+ end
92
+ end
93
+
94
+ def handle_websocket_error(err)
95
+ self.emit :websocket_error, err
96
+ end
97
+
98
+ def handle_websocket_close(event)
99
+ self.emit :websocket_close, event
100
+ end
101
+
102
+ def send_request(request)
103
+ http = Net::HTTP.new(@uri.host, @uri.port)
104
+ response = http.request(request)
105
+ if response.body and !response.body.empty?
106
+ object = MultiJson.load response.body
107
+ else
108
+ object = true
109
+ end
110
+ if response.kind_of? Net::HTTPClientError
111
+ raise Ari::RequestError.new(response.code), (object['message'] || object['error'])
112
+ elsif response.kind_of? Net::HTTPServerError
113
+ raise Ari::ServerError.new(response.code), (object['message'] || object['error'])
114
+ end
115
+ object
116
+ end
117
+
118
+ def self.instance_listeners
119
+ @instance_listeners ||= {}
120
+ @instance_listeners
121
+ end
122
+
123
+ end
124
+ end
@@ -0,0 +1,23 @@
1
+ module Ari
2
+ module Generators
3
+ class Api
4
+
5
+ def initialize(specification)
6
+ @specification = specification
7
+ end
8
+
9
+ def path
10
+ @specification['path'].gsub(/(\{[A-z]*\})/) { "%#{$1}" }
11
+ end
12
+
13
+ def description
14
+ @specification['description']
15
+ end
16
+
17
+ def operations
18
+ @operations ||= @specification['operations'].map { |op| Operation.new(op) }
19
+ end
20
+
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,35 @@
1
+ module Ari
2
+ module Generators
3
+ class Attribute
4
+
5
+ def initialize(specification)
6
+ @specification = specification
7
+ end
8
+
9
+ def name
10
+ @specification['name']
11
+ end
12
+
13
+ def description
14
+ @specification['description']
15
+ end
16
+
17
+ def location
18
+ @specification['paramType']
19
+ end
20
+
21
+ def required?
22
+ @specification['required']
23
+ end
24
+
25
+ def multiple?
26
+ @specification['allowMultiple']
27
+ end
28
+
29
+ def type
30
+ @specification['dataType']
31
+ end
32
+
33
+ end
34
+ end
35
+ end