asterisk-ari-client 0.0.1

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