bigbluebutton-api-ruby 1.3.0 → 1.8.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 +7 -0
- data/.gitignore +2 -1
- data/.rspec +1 -0
- data/.ruby-version +1 -1
- data/.travis.yml +7 -3
- data/CHANGELOG.md +197 -0
- data/Dockerfile +13 -0
- data/Gemfile +2 -2
- data/Gemfile.lock +26 -13
- data/LICENSE +1 -1
- data/README.md +96 -0
- data/Rakefile +8 -4
- data/bigbluebutton-api-ruby.gemspec +19 -12
- data/docker-compose.yml +10 -0
- data/examples/get_version_example.rb +1 -3
- data/examples/join_example.rb +0 -1
- data/examples/prepare.rb +1 -1
- data/features/config.yml.example +5 -9
- data/features/step_definitions/common_steps.rb +3 -4
- data/lib/bigbluebutton_api.rb +111 -32
- data/lib/bigbluebutton_config_layout.rb +3 -1
- data/lib/bigbluebutton_config_xml.rb +8 -5
- data/lib/bigbluebutton_exception.rb +1 -1
- data/lib/bigbluebutton_formatter.rb +4 -2
- data/lib/bigbluebutton_hash_to_xml.rb +3 -1
- data/spec/bigbluebutton_api_0.81_spec.rb +2 -3
- data/spec/bigbluebutton_api_0.9_spec.rb +32 -0
- data/spec/bigbluebutton_api_spec.rb +665 -599
- data/spec/bigbluebutton_config_xml_spec.rb +30 -4
- data/spec/bigbluebutton_formatter_spec.rb +36 -7
- metadata +113 -35
- data/CHANGELOG.rdoc +0 -90
- data/README.rdoc +0 -62
data/lib/bigbluebutton_api.rb
CHANGED
@@ -9,10 +9,11 @@ require 'bigbluebutton_formatter'
|
|
9
9
|
require 'bigbluebutton_modules'
|
10
10
|
require 'bigbluebutton_config_xml'
|
11
11
|
require 'bigbluebutton_config_layout'
|
12
|
+
require 'logger'
|
12
13
|
|
13
14
|
module BigBlueButton
|
14
15
|
|
15
|
-
# This class provides access to the BigBlueButton API. For more details see README.
|
16
|
+
# This class provides access to the BigBlueButton API. For more details see README.
|
16
17
|
#
|
17
18
|
# Sample usage of the API is as follows:
|
18
19
|
# 1. Create a meeting with create_meeting;
|
@@ -44,14 +45,14 @@ module BigBlueButton
|
|
44
45
|
# URL to a BigBlueButton server (e.g. http://demo.bigbluebutton.org/bigbluebutton/api)
|
45
46
|
attr_accessor :url
|
46
47
|
|
47
|
-
#
|
48
|
-
attr_accessor :
|
48
|
+
# Shared secret for this server
|
49
|
+
attr_accessor :secret
|
49
50
|
|
50
51
|
# API version e.g. 0.81
|
51
52
|
attr_accessor :version
|
52
53
|
|
53
|
-
#
|
54
|
-
attr_accessor :
|
54
|
+
# logger to log reponses and infos
|
55
|
+
attr_accessor :logger
|
55
56
|
|
56
57
|
# Maximum wait time for HTTP requests (secs)
|
57
58
|
attr_accessor :timeout
|
@@ -65,22 +66,27 @@ module BigBlueButton
|
|
65
66
|
|
66
67
|
# Initializes an instance
|
67
68
|
# url:: URL to a BigBlueButton server (e.g. http://demo.bigbluebutton.org/bigbluebutton/api)
|
68
|
-
#
|
69
|
+
# secret:: Shared secret for this server
|
69
70
|
# version:: API version e.g. 0.81
|
70
|
-
def initialize(url,
|
71
|
-
@supported_versions = ['0.8', '0.81']
|
71
|
+
def initialize(url, secret, version=nil, logger=nil)
|
72
|
+
@supported_versions = ['0.8', '0.81', '0.9', '1.0']
|
72
73
|
@url = url
|
73
|
-
@
|
74
|
-
@debug = debug
|
74
|
+
@secret = secret
|
75
75
|
@timeout = 10 # default timeout for api requests
|
76
76
|
@request_headers = {} # http headers sent in all requests
|
77
|
-
|
78
|
-
|
77
|
+
@logger = logger
|
78
|
+
if logger.nil?
|
79
|
+
@logger = Logger.new(STDOUT)
|
80
|
+
@logger.level = Logger::INFO
|
81
|
+
end
|
82
|
+
|
83
|
+
version = nil if version && version.strip.empty?
|
84
|
+
@version = nearest_version(version || get_api_version)
|
79
85
|
unless @supported_versions.include?(@version)
|
80
|
-
|
86
|
+
@logger.warn("BigBlueButtonAPI: detected unsupported version, using the closest one that is supported (#{@version})")
|
81
87
|
end
|
82
88
|
|
83
|
-
|
89
|
+
@logger.debug("BigBlueButtonAPI: Using version #{@version}")
|
84
90
|
end
|
85
91
|
|
86
92
|
# Creates a new meeting. Returns the hash with the response or throws BigBlueButtonException
|
@@ -225,7 +231,8 @@ module BigBlueButton
|
|
225
231
|
# in this hash and they will be added to the API call.
|
226
232
|
def join_meeting_url(meeting_id, user_name, password, options={})
|
227
233
|
params = { :meetingID => meeting_id, :password => password, :fullName => user_name }.merge(options)
|
228
|
-
get_url(:join, params)
|
234
|
+
url, data = get_url(:join, params)
|
235
|
+
url
|
229
236
|
end
|
230
237
|
|
231
238
|
# Returns a hash object containing the information of a meeting.
|
@@ -459,6 +466,10 @@ module BigBlueButton
|
|
459
466
|
options[:meetingID] = options[:meetingID].join(",") if options[:meetingID].instance_of?(Array)
|
460
467
|
end
|
461
468
|
|
469
|
+
if options.has_key?(:state)
|
470
|
+
options[:state] = options[:state].join(",") if options[:state].instance_of?(Array)
|
471
|
+
end
|
472
|
+
|
462
473
|
response = send_api_request(:getRecordings, options)
|
463
474
|
|
464
475
|
formatter = BigBlueButtonFormatter.new(response)
|
@@ -467,6 +478,30 @@ module BigBlueButton
|
|
467
478
|
response
|
468
479
|
end
|
469
480
|
|
481
|
+
# Available since BBB v1.1
|
482
|
+
# Update metadata (or other attributes depending on the API implementation) for a given recordID (or set of record IDs).
|
483
|
+
# recordIDs (string, Array):: ID or IDs of the target recordings.
|
484
|
+
# Any of the following values are accepted:
|
485
|
+
# "id1"
|
486
|
+
# "id1,id2,id3"
|
487
|
+
# ["id1"]
|
488
|
+
# ["id1", "id2", "id3"]
|
489
|
+
# meta (String):: Pass one or more metadata values to be update (format is the same as in create call)
|
490
|
+
# options (Hash):: Hash with additional parameters. This method doesn't accept additional
|
491
|
+
# parameters, but if you have a custom API with more parameters, you
|
492
|
+
# can simply pass them in this hash and they will be added to the API call.
|
493
|
+
#
|
494
|
+
# === Example responses
|
495
|
+
#
|
496
|
+
# { :returncode => success, :updated => true }
|
497
|
+
#
|
498
|
+
def update_recordings(recordIDs, meta=nil, options={})
|
499
|
+
recordIDs = recordIDs.join(",") if recordIDs.instance_of?(Array) # ["id1", "id2"] becomes "id1,id2"
|
500
|
+
params = { :recordID => recordIDs, :meta => meta }.merge(options)
|
501
|
+
send_api_request(:updateRecordings, params)
|
502
|
+
end
|
503
|
+
|
504
|
+
|
470
505
|
# Publish and unpublish recordings for a given recordID (or set of record IDs).
|
471
506
|
# recordIDs (string, Array):: ID or IDs of the target recordings.
|
472
507
|
# Any of the following values are accepted:
|
@@ -598,10 +633,15 @@ module BigBlueButton
|
|
598
633
|
response[:returncode]
|
599
634
|
end
|
600
635
|
|
636
|
+
def check_url
|
637
|
+
url, data = get_url(:check)
|
638
|
+
url
|
639
|
+
end
|
640
|
+
|
601
641
|
# API's are equal if all the following attributes are equal.
|
602
642
|
def ==(other)
|
603
643
|
r = true
|
604
|
-
[:url, :supported_versions, :
|
644
|
+
[:url, :supported_versions, :secret, :version, :logger].each do |param|
|
605
645
|
r = r && self.send(param) == other.send(param)
|
606
646
|
end
|
607
647
|
r
|
@@ -622,11 +662,12 @@ module BigBlueButton
|
|
622
662
|
# params (Hash):: The parameters to be passed in the URL
|
623
663
|
def get_url(method, params={})
|
624
664
|
if method == :index
|
625
|
-
return @url
|
665
|
+
return @url, nil
|
666
|
+
elsif method == :check
|
667
|
+
baseurl = URI.join(@url, "/").to_s
|
668
|
+
return "#{baseurl}check", nil
|
626
669
|
end
|
627
670
|
|
628
|
-
url = "#{@url}/#{method}?"
|
629
|
-
|
630
671
|
# stringify and escape all params
|
631
672
|
params.delete_if { |k, v| v.nil? } unless params.nil?
|
632
673
|
# some API calls require the params to be sorted
|
@@ -634,16 +675,22 @@ module BigBlueButton
|
|
634
675
|
params = params.inject({}){ |memo,(k,v)| memo[k.to_sym] = v; memo }
|
635
676
|
params = Hash[params.sort]
|
636
677
|
params_string = ""
|
637
|
-
params_string = params.map{ |k,v| "#{k}=" +
|
678
|
+
params_string = params.map{ |k,v| "#{k}=" + URI.encode_www_form_component(v.to_s) unless k.nil? || v.nil? }.join("&")
|
638
679
|
|
639
680
|
# checksum calc
|
640
|
-
checksum_param = params_string + @
|
681
|
+
checksum_param = params_string + @secret
|
641
682
|
checksum_param = method.to_s + checksum_param
|
642
683
|
checksum = Digest::SHA1.hexdigest(checksum_param)
|
643
684
|
|
644
|
-
|
645
|
-
|
646
|
-
|
685
|
+
if method == :setConfigXML
|
686
|
+
params_string = "checksum=#{checksum}&#{params_string}"
|
687
|
+
return "#{@url}/#{method}", params_string
|
688
|
+
else
|
689
|
+
url = "#{@url}/#{method}?"
|
690
|
+
url += "#{params_string}&" unless params_string.empty?
|
691
|
+
url += "checksum=#{checksum}"
|
692
|
+
return url, nil
|
693
|
+
end
|
647
694
|
end
|
648
695
|
|
649
696
|
# Performs an API call.
|
@@ -660,7 +707,9 @@ module BigBlueButton
|
|
660
707
|
# raw (boolean):: If true, returns the data as it was received. Will not parse it into a Hash,
|
661
708
|
# check for errors or throw exceptions.
|
662
709
|
def send_api_request(method, params={}, data=nil, raw=false)
|
663
|
-
|
710
|
+
# if the method returns a body, use it as the data in the post request
|
711
|
+
url, body = get_url(method, params)
|
712
|
+
data = body if body
|
664
713
|
|
665
714
|
@http_response = send_request(url, data)
|
666
715
|
return {} if @http_response.body.empty?
|
@@ -699,29 +748,59 @@ module BigBlueButton
|
|
699
748
|
# Otherwise uses GET
|
700
749
|
def send_request(url, data=nil)
|
701
750
|
begin
|
702
|
-
|
751
|
+
@logger.debug("BigBlueButtonAPI: URL request = #{url}")
|
703
752
|
url_parsed = URI.parse(url)
|
704
753
|
http = Net::HTTP.new(url_parsed.host, url_parsed.port)
|
705
754
|
http.open_timeout = @timeout
|
706
755
|
http.read_timeout = @timeout
|
756
|
+
http.use_ssl = true if url_parsed.scheme.downcase == 'https'
|
757
|
+
|
707
758
|
if data.nil?
|
708
759
|
response = http.get(url_parsed.request_uri, @request_headers)
|
709
760
|
else
|
710
|
-
|
711
|
-
opts = { 'Content-Type' => '
|
761
|
+
@logger.debug("BigBlueButtonAPI: Sending as a POST request with data.size = #{data.size}")
|
762
|
+
opts = { 'Content-Type' => 'application/xml' }.merge @request_headers
|
712
763
|
response = http.post(url_parsed.request_uri, data, opts)
|
713
764
|
end
|
714
|
-
|
765
|
+
@logger.info("BigBlueButtonAPI: request=#{url} response_status=#{response.class.name} response_code=#{response.code} message_key=#{response.message}")
|
766
|
+
@logger.debug("BigBlueButtonAPI: URL response = #{response.body}")
|
715
767
|
|
716
|
-
rescue
|
717
|
-
|
768
|
+
rescue Timeout::Error => error
|
769
|
+
exception = BigBlueButtonException.new("Timeout error. Your server is probably down: \"#{@url}\". Error: #{error}")
|
770
|
+
exception.key = 'TimeoutError'
|
771
|
+
raise exception
|
718
772
|
|
719
773
|
rescue Exception => error
|
720
|
-
|
774
|
+
exception = BigBlueButtonException.new("Connection error. Your URL is probably incorrect: \"#{@url}\". Error: #{error}")
|
775
|
+
exception.key = 'IncorrectUrlError'
|
776
|
+
raise exception
|
721
777
|
end
|
722
778
|
|
723
779
|
response
|
724
780
|
end
|
725
781
|
|
782
|
+
def nearest_version(target)
|
783
|
+
version = target
|
784
|
+
|
785
|
+
# 0.81 in BBB is actually < than 0.9, but not when comparing here
|
786
|
+
# so normalize x.xx versions to x.x.x
|
787
|
+
match = version.match(/(\d)\.(\d)(\d)/)
|
788
|
+
version = "#{match[1]}.#{match[2]}.#{match[3]}" if match
|
789
|
+
|
790
|
+
# we don't allow older versions than the one supported, use an old version of the gem for that
|
791
|
+
if Gem::Version.new(version) < Gem::Version.new(@supported_versions[0])
|
792
|
+
exception = BigBlueButtonException.new("BigBlueButton error: Invalid API version #{version}. Supported versions: #{@supported_versions.join(', ')}")
|
793
|
+
exception.key = 'APIVersionError'
|
794
|
+
raise exception
|
795
|
+
|
796
|
+
# allow newer versions by using the newest one we support
|
797
|
+
elsif Gem::Version.new(version) > Gem::Version.new(@supported_versions.last)
|
798
|
+
@supported_versions.last
|
799
|
+
|
800
|
+
else
|
801
|
+
target
|
802
|
+
end
|
803
|
+
end
|
804
|
+
|
726
805
|
end
|
727
806
|
end
|
@@ -29,7 +29,9 @@ module BigBlueButton
|
|
29
29
|
begin
|
30
30
|
@xml = XmlSimple.xml_in(xml, opts)
|
31
31
|
rescue Exception => e
|
32
|
-
|
32
|
+
exception = BigBlueButton::BigBlueButtonException.new("Error parsing the layouts XML. Error: #{e.message}")
|
33
|
+
exception.key = 'XMLParsingError'
|
34
|
+
raise exception
|
33
35
|
end
|
34
36
|
end
|
35
37
|
|
@@ -25,15 +25,17 @@ module BigBlueButton
|
|
25
25
|
attr_accessor :xml
|
26
26
|
|
27
27
|
def initialize(xml)
|
28
|
-
@
|
28
|
+
@original_string = nil
|
29
29
|
@xml = nil
|
30
30
|
unless xml.nil?
|
31
31
|
opts = { 'ForceArray' => false, 'KeepRoot' => true }
|
32
32
|
begin
|
33
33
|
@xml = XmlSimple.xml_in(xml, opts)
|
34
|
-
@
|
34
|
+
@original_string = self.as_string.clone
|
35
35
|
rescue Exception => e
|
36
|
-
|
36
|
+
exception = BigBlueButton::BigBlueButtonException.new("Error parsing the config XML. Error: #{e.message}")
|
37
|
+
exception.key = 'XMLParsingError'
|
38
|
+
raise exception
|
37
39
|
end
|
38
40
|
end
|
39
41
|
end
|
@@ -60,7 +62,8 @@ module BigBlueButton
|
|
60
62
|
if tag
|
61
63
|
attr = find_attribute(tag, attr_name)
|
62
64
|
if attr
|
63
|
-
|
65
|
+
# saves always as string
|
66
|
+
tag[attr_name] = value.is_a?(String) ? value : value.to_s
|
64
67
|
else
|
65
68
|
nil
|
66
69
|
end
|
@@ -75,7 +78,7 @@ module BigBlueButton
|
|
75
78
|
|
76
79
|
def is_modified?
|
77
80
|
@xml and
|
78
|
-
|
81
|
+
self.as_string != @original_string
|
79
82
|
end
|
80
83
|
|
81
84
|
protected
|
@@ -22,7 +22,7 @@ module BigBlueButton
|
|
22
22
|
unless @hash.has_key?(key)
|
23
23
|
0
|
24
24
|
else
|
25
|
-
@hash[key] = @hash[key].to_i
|
25
|
+
@hash[key] = @hash[key].to_i rescue 0
|
26
26
|
end
|
27
27
|
end
|
28
28
|
|
@@ -49,7 +49,9 @@ module BigBlueButton
|
|
49
49
|
if value.is_a?(Numeric)
|
50
50
|
result = value == 0 ? nil : DateTime.parse(Time.at(value/1000.0).to_s)
|
51
51
|
else
|
52
|
-
if value.
|
52
|
+
if (value.is_a?(Hash) || value.is_a?(Array)) && value.empty?
|
53
|
+
result = nil
|
54
|
+
elsif value.is_a?(String) && (value.empty? || value.downcase == 'null')
|
53
55
|
result = nil
|
54
56
|
else
|
55
57
|
# note: just in case the value comes as a string in the format: "Thu Sep 01 17:51:42 UTC 2011"
|
@@ -10,7 +10,9 @@ module BigBlueButton
|
|
10
10
|
hash = XmlSimple.xml_in(xml_io, opts)
|
11
11
|
return symbolize_keys(hash)
|
12
12
|
rescue Exception => e
|
13
|
-
|
13
|
+
exception = BigBlueButtonException.new("Impossible to convert XML to hash. Error: #{e.message}")
|
14
|
+
exception.key = 'XMLConversionError'
|
15
|
+
raise exception
|
14
16
|
end
|
15
17
|
end
|
16
18
|
|
@@ -5,10 +5,9 @@ describe BigBlueButton::BigBlueButtonApi do
|
|
5
5
|
|
6
6
|
# default variables and API object for all tests
|
7
7
|
let(:url) { "http://server.com" }
|
8
|
-
let(:
|
8
|
+
let(:secret) { "1234567890abcdefghijkl" }
|
9
9
|
let(:version) { "0.81" }
|
10
|
-
let(:
|
11
|
-
let(:api) { BigBlueButton::BigBlueButtonApi.new(url, salt, version, debug) }
|
10
|
+
let(:api) { BigBlueButton::BigBlueButtonApi.new(url, secret, version) }
|
12
11
|
|
13
12
|
describe "#get_default_config_xml" do
|
14
13
|
let(:response) { "<response><returncode>1</returncode></response>" }
|
@@ -0,0 +1,32 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
# Tests for BBB API version 0.81
|
4
|
+
describe BigBlueButton::BigBlueButtonApi do
|
5
|
+
|
6
|
+
# default variables and API object for all tests
|
7
|
+
let(:url) { "http://server.com" }
|
8
|
+
let(:secret) { "1234567890abcdefghijkl" }
|
9
|
+
let(:version) { "0.9" }
|
10
|
+
let(:api) { BigBlueButton::BigBlueButtonApi.new(url, secret, version) }
|
11
|
+
|
12
|
+
describe "#create_meeting" do
|
13
|
+
context "accepts the new parameters" do
|
14
|
+
let(:req_params) {
|
15
|
+
{ :name => "name", :meetingID => "meeting-id",
|
16
|
+
:moderatorOnlyMessage => "my-msg", :autoStartRecording => "false",
|
17
|
+
:allowStartStopRecording => "true"
|
18
|
+
}
|
19
|
+
}
|
20
|
+
|
21
|
+
before { api.should_receive(:send_api_request).with(:create, req_params) }
|
22
|
+
it {
|
23
|
+
options = {
|
24
|
+
:moderatorOnlyMessage => "my-msg",
|
25
|
+
:autoStartRecording => "false",
|
26
|
+
:allowStartStopRecording => "true"
|
27
|
+
}
|
28
|
+
api.create_meeting("name", "meeting-id", options)
|
29
|
+
}
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|