bigbluebutton-api-ruby 1.2.0 → 1.3.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.
@@ -0,0 +1,56 @@
1
+ require "xmlsimple"
2
+
3
+ module BigBlueButton
4
+
5
+ # A helper class to work with layout definition files.
6
+ # You set an xml file on it (usually obtained from a property of in the XML obtained from
7
+ # BigBlueButtonApi#get_default_config_xml), use it to work with the layouts, and then get
8
+ # the new xml from this class.
9
+ #
10
+ # === Usage example:
11
+ #
12
+ # xml = api.get_default_config_xml
13
+ # config_xml = BigBlueButton::BigBlueButtonConfigXml.new(xml)
14
+ # url = config_xml.get_attribute("LayoutModule", "layoutConfig", true)
15
+ # # send a request to 'url' to fetch the xml file into 'response'
16
+ #
17
+ # layout_config = BigBlueButton::BigBlueButtonConfigLayout.new(response)
18
+ # layout_config.get_available_layouts
19
+ #
20
+ class BigBlueButtonConfigLayout
21
+
22
+ attr_accessor :xml
23
+
24
+ # xml (string):: The XML that has the definition of all layouts, usually fetched from
25
+ # the web conference server.
26
+ def initialize(xml)
27
+ @xml = nil
28
+ opts = { 'ForceArray' => false, 'KeepRoot' => true }
29
+ begin
30
+ @xml = XmlSimple.xml_in(xml, opts)
31
+ rescue Exception => e
32
+ raise BigBlueButton::BigBlueButtonException.new("Error parsing the layouts XML. Error: #{e.message}")
33
+ end
34
+ end
35
+
36
+ # Returns an array with the name of each layout available in the XML.
37
+ # Will return only unique names, ordered the way they are ordered in the XML.
38
+ # Returns nil if the XML is not properly formatted and an empty
39
+ # array if there are no layouts in the file.
40
+ def get_available_layouts
41
+ if xml_has_layouts
42
+ xml["layouts"]["layout"].map{ |l| l["name"] }.uniq
43
+ else
44
+ nil
45
+ end
46
+ end
47
+
48
+ protected
49
+
50
+ def xml_has_layouts
51
+ @xml and @xml["layouts"] and @xml["layouts"]["layout"]
52
+ end
53
+
54
+ end
55
+
56
+ end
@@ -0,0 +1,122 @@
1
+ require "xmlsimple"
2
+
3
+ module BigBlueButton
4
+
5
+ # A helper class to work with config.xml files.
6
+ # You set an xml file on it (usually obtained via BigBlueButtonApi#get_default_config_xml),
7
+ # use it to modify this xml, and then get the new xml from this class (usually to set in the
8
+ # server using BigBlueButtonApi#set_config_xml).
9
+ #
10
+ # === Usage example:
11
+ #
12
+ # xml = api.get_default_config_xml
13
+ # config_xml = BigBlueButton::BigBlueButtonConfigXml.new(xml)
14
+ #
15
+ # # change the xml a bit
16
+ # config_xml.set_attribute("skinning", "enabled", "false", false)
17
+ # config_xml.set_attribute("layout", "defaultLayout", "Webinar", false)
18
+ # config_xml.set_attribute("layout", "showLayoutTools", "false", false)
19
+ #
20
+ # # set the new xml in the server
21
+ # api.set_config_xml("meeting-id", config_xml)
22
+ #
23
+ class BigBlueButtonConfigXml
24
+
25
+ attr_accessor :xml
26
+
27
+ def initialize(xml)
28
+ @original_xml = nil
29
+ @xml = nil
30
+ unless xml.nil?
31
+ opts = { 'ForceArray' => false, 'KeepRoot' => true }
32
+ begin
33
+ @xml = XmlSimple.xml_in(xml, opts)
34
+ @original_xml = Marshal.load(Marshal.dump(@xml))
35
+ rescue Exception => e
36
+ raise BigBlueButton::BigBlueButtonException.new("Error parsing the config XML. Error: #{e.message}")
37
+ end
38
+ end
39
+ end
40
+
41
+ def get_attribute(finder, attr_name, is_module=true)
42
+ if is_module
43
+ tag = find_module(finder)
44
+ else
45
+ tag = find_tag(finder)
46
+ end
47
+ if tag
48
+ find_attribute(tag, attr_name)
49
+ else
50
+ nil
51
+ end
52
+ end
53
+
54
+ def set_attribute(finder, attr_name, value, is_module=true)
55
+ if is_module
56
+ tag = find_module(finder)
57
+ else
58
+ tag = find_tag(finder)
59
+ end
60
+ if tag
61
+ attr = find_attribute(tag, attr_name)
62
+ if attr
63
+ tag[attr_name] = value
64
+ else
65
+ nil
66
+ end
67
+ else
68
+ nil
69
+ end
70
+ end
71
+
72
+ def as_string
73
+ XmlSimple.xml_out(@xml, { 'RootName' => nil, 'XmlDeclaration' => false, 'NoIndent' => true })
74
+ end
75
+
76
+ def is_modified?
77
+ @xml and
78
+ @xml != @original_xml
79
+ end
80
+
81
+ protected
82
+
83
+ def find_module(module_name)
84
+ if xml_has_modules
85
+ modules = @xml["config"]["modules"]["module"]
86
+ modules = [modules] unless modules.is_a?(Array)
87
+ modules.each do |mod|
88
+ if mod["name"] == module_name
89
+ return mod
90
+ end
91
+ end
92
+ end
93
+ nil
94
+ end
95
+
96
+ def find_tag(name)
97
+ if xml_has_config
98
+ @xml["config"][name]
99
+ end
100
+ end
101
+
102
+ def find_attribute(mod, attr_name)
103
+ if mod and mod[attr_name]
104
+ return mod[attr_name]
105
+ else
106
+ return nil
107
+ end
108
+ end
109
+
110
+ def xml_has_modules
111
+ xml_has_config and
112
+ @xml["config"]["modules"] and
113
+ @xml["config"]["modules"]["module"]
114
+ end
115
+
116
+ def xml_has_config
117
+ @xml and @xml["config"]
118
+ end
119
+
120
+ end
121
+
122
+ end
@@ -52,7 +52,7 @@ module BigBlueButton
52
52
  if value.downcase == "null"
53
53
  result = nil
54
54
  else
55
- # note: BBB 0.7 uses strings in the format: "Thu Sep 01 17:51:42 UTC 2011"
55
+ # note: just in case the value comes as a string in the format: "Thu Sep 01 17:51:42 UTC 2011"
56
56
  result = DateTime.parse(value)
57
57
  end
58
58
  end
@@ -92,11 +92,17 @@ module BigBlueButton
92
92
  def self.format_meeting(meeting)
93
93
  f = BigBlueButtonFormatter.new(meeting)
94
94
  f.to_string(:meetingID)
95
+ f.to_string(:meetingName)
95
96
  f.to_string(:moderatorPW)
96
97
  f.to_string(:attendeePW)
97
98
  f.to_boolean(:hasBeenForciblyEnded)
98
99
  f.to_boolean(:running)
99
100
  f.to_int(:createTime) if meeting.has_key?(:createTime)
101
+ f.to_string(:dialNumber)
102
+ f.to_int(:voiceBridge)
103
+ f.to_int(:participantCount)
104
+ f.to_int(:listenerCount)
105
+ f.to_int(:videoCount)
100
106
  meeting
101
107
  end
102
108
 
@@ -152,18 +158,18 @@ module BigBlueButton
152
158
  #
153
159
  # Other examples:
154
160
  #
155
- # Hash:
161
+ # # Hash:
156
162
  # { :name => "Test", :attendees => {} }
157
- # Result:
163
+ # # Result:
158
164
  # { :name => "Test", :attendees => [] }
159
165
  #
160
- # Hash:
166
+ # # Hash:
161
167
  # { :name => "Test", :attendees => { :attendee => { :name => "attendee1" } } }
162
- # Result:
168
+ # # Result:
163
169
  # { :name => "Test", :attendees => [ { :name => "attendee1" } ] }
164
170
  #
165
171
  def flatten_objects(first, second)
166
- if @hash[first].empty?
172
+ if !@hash[first] or @hash[first].empty?
167
173
  collection = []
168
174
  else
169
175
  node = @hash[first][second]
@@ -0,0 +1,35 @@
1
+ require 'xmlsimple'
2
+
3
+ module BigBlueButton
4
+ class BigBlueButtonHash < Hash
5
+ class << self
6
+ def from_xml(xml_io)
7
+ begin
8
+ # we'll not use 'KeyToSymbol' because it doesn't symbolize the keys for node attributes
9
+ opts = { 'ForceArray' => false, 'ForceContent' => false } #
10
+ hash = XmlSimple.xml_in(xml_io, opts)
11
+ return symbolize_keys(hash)
12
+ rescue Exception => e
13
+ raise BigBlueButtonException.new("Impossible to convert XML to hash. Error: #{e.message}")
14
+ end
15
+ end
16
+
17
+ def symbolize_keys(arg)
18
+ case arg
19
+ when Array
20
+ arg.map { |elem| symbolize_keys elem }
21
+ when Hash
22
+ Hash[
23
+ arg.map { |key, value|
24
+ k = key.is_a?(String) ? key.to_sym : key
25
+ v = symbolize_keys value
26
+ [k,v]
27
+ }]
28
+ else
29
+ arg
30
+ end
31
+ end
32
+
33
+ end
34
+ end
35
+ end
@@ -2,8 +2,7 @@ require "base64"
2
2
 
3
3
  module BigBlueButton
4
4
 
5
- # An object to store the modules configuration to be passed in
6
- # BigBlueButtonApi#create_meeting().
5
+ # A class to store the modules configuration to be passed in BigBlueButtonApi#create_meeting().
7
6
  #
8
7
  # === Usage example:
9
8
  #
@@ -0,0 +1,154 @@
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(:salt) { "1234567890abcdefghijkl" }
9
+ let(:version) { "0.81" }
10
+ let(:debug) { false }
11
+ let(:api) { BigBlueButton::BigBlueButtonApi.new(url, salt, version, debug) }
12
+
13
+ describe "#get_default_config_xml" do
14
+ let(:response) { "<response><returncode>1</returncode></response>" }
15
+ let(:response_asObject) { {"response" => {"returncode" => "1" } } }
16
+
17
+ context "with response as a string" do
18
+ context "and without non standard options" do
19
+ before { api.should_receive(:send_api_request).with(:getDefaultConfigXML, {}, nil, true).and_return(response) }
20
+ it { api.get_default_config_xml() }
21
+ it { api.get_default_config_xml().should == response }
22
+ it { api.get_default_config_xml(false).should_not == response_asObject }
23
+ end
24
+
25
+ context "and with non standard options" do
26
+ let(:params_in) {
27
+ { :anything1 => "anything-1", :anything2 => 2 }
28
+ }
29
+ before { api.should_receive(:send_api_request).with(:getDefaultConfigXML, params_in, nil, true).and_return(response) }
30
+ it { api.get_default_config_xml(false, params_in) }
31
+ it { api.get_default_config_xml(false, params_in).should == response }
32
+ it { api.get_default_config_xml(false, params_in).should_not == response_asObject }
33
+ end
34
+ end
35
+
36
+ context "with response as a object" do
37
+ context "and without non standard options" do
38
+ before { api.should_receive(:send_api_request).with(:getDefaultConfigXML, {}, nil, true).and_return(response) }
39
+ it { api.get_default_config_xml(true) }
40
+ it { api.get_default_config_xml(true).should == response_asObject }
41
+ it { api.get_default_config_xml(true).should_not == response }
42
+ end
43
+
44
+ context "and with non standard options" do
45
+ let(:params_in) {
46
+ { :anything1 => "anything-1", :anything2 => 2 }
47
+ }
48
+ before { api.should_receive(:send_api_request).with(:getDefaultConfigXML, params_in, nil, true).and_return(response) }
49
+ it { api.get_default_config_xml(true, params_in) }
50
+ it { api.get_default_config_xml(true, params_in).should == response_asObject }
51
+ it { api.get_default_config_xml(true, params_in).should_not == response }
52
+ end
53
+ end
54
+ end
55
+
56
+ describe "#set_config_xml" do
57
+ let(:configToken) { "asdfl234kjasdfsadfy" }
58
+ let(:meeting_id) { "meeting-id" }
59
+ let(:xml) { "<response><returncode>SUCCESS</returncode><configToken>asdfl234kjasdfsadfy</configToken></response>" }
60
+ let(:response) {
61
+ { :returncode => "SUCCESS", :configToken => configToken }
62
+ }
63
+
64
+ context "without non standard options" do
65
+ let(:params) {
66
+ { :meetingID => meeting_id, :configXML => xml }
67
+ }
68
+
69
+ before { api.should_receive(:send_api_request).with(:setConfigXML, params, xml).and_return(response) }
70
+ it { api.set_config_xml(meeting_id, xml) }
71
+ it { api.set_config_xml(meeting_id, xml).should == configToken }
72
+ end
73
+
74
+ context "with non standard options" do
75
+ let(:params_in) {
76
+ { :anything1 => "anything-1", :anything2 => 2 }
77
+ }
78
+ let(:params_out) {
79
+ { :meetingID => meeting_id, :configXML => xml, :anything1 => "anything-1", :anything2 => 2 }
80
+ }
81
+
82
+ before { api.should_receive(:send_api_request).with(:setConfigXML, params_out, xml).and_return(response) }
83
+ it { api.set_config_xml(meeting_id, xml, params_in) }
84
+ it { api.set_config_xml(meeting_id, xml, params_in).should == configToken }
85
+ end
86
+ end
87
+
88
+ describe "#get_available_layouts" do
89
+ let(:config_xml) { # a simplified config.xml file
90
+ "<config>
91
+ <modules>
92
+ <module name=\"LayoutModule\" url=\"http://test-server.org/client/LayoutModule.swf?v=4357\"
93
+ uri=\"rtmp://test-server.org/bigbluebutton\"
94
+ layoutConfig=\"http://test-server.org/client/conf/layout.xml\"
95
+ enableEdit=\"false\"/>
96
+ </modules>
97
+ </config>"
98
+ }
99
+ let(:layouts_xml) { # a simplified layouts.xml file
100
+ "<layouts>
101
+ <layout name=\"Default\" default=\"true\">
102
+ <window name=\"NotesWindow\" hidden=\"true\" width=\"0.7\" height=\"1\" x=\"0\" y=\"0\" draggable=\"false\" resizable=\"false\"/>
103
+ </layout>
104
+ <layout name=\"Video Chat\">
105
+ <window name=\"NotesWindow\" hidden=\"true\" width=\"0.7\" height=\"1\" x=\"0\" y=\"0\" draggable=\"false\" resizable=\"false\"/>
106
+ </layout>
107
+ </layouts>"
108
+ }
109
+
110
+ context "when an XML is passed" do
111
+ before {
112
+ response = double("Net::HTTPResponse")
113
+ response.stub(:body).and_return(layouts_xml)
114
+ api.should_receive(:send_request)
115
+ .with("http://test-server.org/client/conf/layout.xml")
116
+ .and_return(response)
117
+ }
118
+ subject { api.get_available_layouts(config_xml) }
119
+ it { should be_instance_of(Array) }
120
+ it { subject.count.should be(2) }
121
+ it { should include("Default") }
122
+ it { should include("Video Chat") }
123
+ end
124
+
125
+ context "when no XML is passed" do
126
+ before {
127
+ api.should_receive(:get_default_config_xml)
128
+ .and_return(config_xml)
129
+ response = double("Net::HTTPResponse")
130
+ response.stub(:body).and_return(layouts_xml)
131
+ api.should_receive(:send_request)
132
+ .with("http://test-server.org/client/conf/layout.xml")
133
+ .and_return(response)
134
+ }
135
+ subject { api.get_available_layouts }
136
+ it { should be_instance_of(Array) }
137
+ it { subject.count.should be(2) }
138
+ it { should include("Default") }
139
+ it { should include("Video Chat") }
140
+ end
141
+ end
142
+
143
+ describe "#get_default_layouts" do
144
+ subject { api.get_default_layouts }
145
+ it { should be_instance_of(Array) }
146
+ it { should include("Default") }
147
+ it { should include("Video Chat") }
148
+ it { should include("Meeting") }
149
+ it { should include("Webinar") }
150
+ it { should include("Lecture assistant") }
151
+ it { should include("Lecture") }
152
+ end
153
+
154
+ end
@@ -1,12 +1,13 @@
1
1
  require 'spec_helper'
2
2
 
3
- # Note: this file tests the functioning of the API object using BBB API version 0.7 as a basis
3
+ # Note: Uses version 0.8 by default. For things that only exist in newer versions,
4
+ # there are separate files with more tests.
4
5
  describe BigBlueButton::BigBlueButtonApi do
5
6
 
6
7
  # default variables and API object for all tests
7
8
  let(:url) { "http://server.com" }
8
9
  let(:salt) { "1234567890abcdefghijkl" }
9
- let(:version) { "0.7" }
10
+ let(:version) { "0.8" }
10
11
  let(:debug) { false }
11
12
  let(:api) { BigBlueButton::BigBlueButtonApi.new(url, salt, version, debug) }
12
13
 
@@ -18,15 +19,15 @@ describe BigBlueButton::BigBlueButtonApi do
18
19
  it { subject.version.should == version }
19
20
  it { subject.debug.should == debug }
20
21
  it { subject.timeout.should == 10 }
21
- it { subject.supported_versions.should include("0.7") }
22
22
  it { subject.supported_versions.should include("0.8") }
23
+ it { subject.supported_versions.should include("0.81") }
23
24
  it { subject.request_headers.should == {} }
24
25
  end
25
26
 
26
27
  context "when the version is not informed, get it from the BBB server" do
27
- before { BigBlueButton::BigBlueButtonApi.any_instance.should_receive(:get_api_version).and_return("0.7") }
28
+ before { BigBlueButton::BigBlueButtonApi.any_instance.should_receive(:get_api_version).and_return("0.8") }
28
29
  subject { BigBlueButton::BigBlueButtonApi.new(url, salt, nil) }
29
- it { subject.version.should == "0.7" }
30
+ it { subject.version.should == "0.8" }
30
31
  end
31
32
 
32
33
  it "when the version is not supported raise an error" do
@@ -37,7 +38,7 @@ describe BigBlueButton::BigBlueButtonApi do
37
38
 
38
39
  context "current supported versions" do
39
40
  subject { BigBlueButton::BigBlueButtonApi.new(url, salt) }
40
- it { subject.supported_versions.should == ["0.7", "0.8"] }
41
+ it { subject.supported_versions.should == ["0.8", "0.81"] }
41
42
  end
42
43
  end
43
44
 
@@ -87,16 +88,84 @@ describe BigBlueButton::BigBlueButtonApi do
87
88
  api.create_meeting("name", "meeting-id", params)
88
89
  }
89
90
  end
91
+
92
+ context "with modules" do
93
+ let(:req_params) {
94
+ { :name => "name", :meetingID => "meeting-id", :moderatorPW => "mp", :attendeePW => "ap" }
95
+ }
96
+ let(:req_response) {
97
+ { :meetingID => 123, :moderatorPW => 111, :attendeePW => 222, :hasBeenForciblyEnded => "FALSE", :createTime => "123123123" }
98
+ }
99
+ let(:final_response) {
100
+ { :meetingID => "123", :moderatorPW => "111", :attendeePW => "222", :hasBeenForciblyEnded => false, :createTime => 123123123 }
101
+ }
102
+ let(:modules) {
103
+ m = BigBlueButton::BigBlueButtonModules.new
104
+ m.add_presentation(:url, "http://www.samplepdf.com/sample.pdf")
105
+ m.add_presentation(:url, "http://www.samplepdf.com/sample2.pdf")
106
+ m.add_presentation(:base64, "JVBERi0xLjQKJ....[clipped here]....0CiUlRU9GCg==", "first-class.pdf")
107
+ m
108
+ }
109
+
110
+ before {
111
+ api.should_receive(:send_api_request).with(:create, req_params, modules.to_xml).
112
+ and_return(req_response)
113
+ }
114
+ subject {
115
+ options = { :moderatorPW => "mp", :attendeePW => "ap" }
116
+ api.create_meeting("name", "meeting-id", options, modules)
117
+ }
118
+ it { subject.should == final_response }
119
+ end
120
+
121
+ context "without modules" do
122
+ let(:req_params) {
123
+ { :name => "name", :meetingID => "meeting-id", :moderatorPW => "mp", :attendeePW => "ap",
124
+ :welcome => "Welcome!", :dialNumber => 12345678, :logoutURL => "http://example.com",
125
+ :maxParticipants => 25, :voiceBridge => 12345, :record => "true", :duration => 20,
126
+ :meta_1 => "meta1", :meta_2 => "meta2" }
127
+ }
128
+ let(:req_response) {
129
+ { :meetingID => 123, :moderatorPW => 111, :attendeePW => 222, :hasBeenForciblyEnded => "FALSE", :createTime => "123123123" }
130
+ }
131
+ let(:final_response) {
132
+ { :meetingID => "123", :moderatorPW => "111", :attendeePW => "222", :hasBeenForciblyEnded => false, :createTime => 123123123 }
133
+ }
134
+
135
+ before { api.should_receive(:send_api_request).with(:create, req_params).and_return(req_response) }
136
+ subject {
137
+ options = { :moderatorPW => "mp", :attendeePW => "ap", :welcome => "Welcome!", :dialNumber => 12345678,
138
+ :logoutURL => "http://example.com", :maxParticipants => 25, :voiceBridge => 12345, :record => true,
139
+ :duration => 20, :meta_1 => "meta1", :meta_2 => "meta2" }
140
+ api.create_meeting("name", "meeting-id", options)
141
+ }
142
+ it { subject.should == final_response }
143
+ end
90
144
  end
91
145
 
92
146
  describe "#end_meeting" do
93
147
  let(:meeting_id) { "meeting-id" }
94
148
  let(:moderator_password) { "password" }
95
- let(:params) { { :meetingID => meeting_id, :password => moderator_password } }
96
- let(:response) { "anything" }
97
149
 
98
- before { api.should_receive(:send_api_request).with(:end, params).and_return(response) }
99
- it { api.end_meeting(meeting_id, moderator_password).should == response }
150
+ context "standard case" do
151
+ let(:params) { { :meetingID => meeting_id, :password => moderator_password } }
152
+ let(:response) { "anything" }
153
+
154
+ before { api.should_receive(:send_api_request).with(:end, params).and_return(response) }
155
+ it { api.end_meeting(meeting_id, moderator_password).should == response }
156
+ end
157
+
158
+ context "accepts non standard options" do
159
+ let(:params_in) {
160
+ { :anything1 => "anything-1", :anything2 => 2 }
161
+ }
162
+ let(:params_out) {
163
+ { :meetingID => meeting_id, :password => moderator_password,
164
+ :anything1 => "anything-1", :anything2 => 2 }
165
+ }
166
+ before { api.should_receive(:send_api_request).with(:end, params_out) }
167
+ it { api.end_meeting(meeting_id, moderator_password, params_in) }
168
+ end
100
169
  end
101
170
 
102
171
  describe "#is_meeting_running?" do
@@ -114,18 +183,29 @@ describe BigBlueButton::BigBlueButtonApi do
114
183
  before { api.should_receive(:send_api_request).with(:isMeetingRunning, params).and_return(response) }
115
184
  it { api.is_meeting_running?(meeting_id).should == false }
116
185
  end
186
+
187
+ context "accepts non standard options" do
188
+ let(:params_in) {
189
+ { :anything1 => "anything-1", :anything2 => 2 }
190
+ }
191
+ let(:params_out) {
192
+ { :meetingID => meeting_id, :anything1 => "anything-1", :anything2 => 2 }
193
+ }
194
+ before { api.should_receive(:send_api_request).with(:isMeetingRunning, params_out) }
195
+ it { api.is_meeting_running?(meeting_id, params_in) }
196
+ end
117
197
  end
118
198
 
119
199
  describe "#join_meeting_url" do
120
200
  context "standard case" do
121
201
  let(:params) {
122
202
  { :meetingID => "meeting-id", :password => "pw", :fullName => "Name",
123
- :userID => "id123", :webVoiceConf => 12345678 }
203
+ :userID => "id123", :webVoiceConf => 12345678, :createTime => 9876543 }
124
204
  }
125
205
 
126
206
  before { api.should_receive(:get_url).with(:join, params).and_return("test-url") }
127
207
  it {
128
- options = { :userID => "id123", :webVoiceConf => 12345678 }
208
+ options = { :userID => "id123", :webVoiceConf => 12345678, :createTime => 9876543 }
129
209
  api.join_meeting_url("meeting-id", "Name", "pw", options).should == "test-url"
130
210
  }
131
211
  end
@@ -140,82 +220,87 @@ describe BigBlueButton::BigBlueButtonApi do
140
220
  end
141
221
  end
142
222
 
143
- describe "#join_meeting" do
223
+ describe "#get_meeting_info" do
224
+ let(:meeting_id) { "meeting-id" }
225
+ let(:password) { "password" }
226
+
144
227
  context "standard case" do
145
- let(:params) {
146
- { :meetingID => "meeting-id", :password => "pw", :fullName => "Name",
147
- :userID => "id123", :webVoiceConf => 12345678 }
148
- }
228
+ let(:params) { { :meetingID => meeting_id, :password => password } }
229
+
230
+ let(:attendee1) { { :userID => 123, :fullName => "Dexter Morgan", :role => "MODERATOR" } }
231
+ let(:attendee2) { { :userID => "id2", :fullName => "Cameron", :role => "VIEWER" } }
232
+ let(:response) {
233
+ { :meetingID => 123, :moderatorPW => 111, :attendeePW => 222, :hasBeenForciblyEnded => "FALSE",
234
+ :running => "TRUE", :startTime => "Thu Sep 01 17:51:42 UTC 2011", :endTime => "null",
235
+ :returncode => true, :attendees => { :attendee => [ attendee1, attendee2 ] },
236
+ :messageKey => "mkey", :message => "m", :participantCount => "50", :moderatorCount => "3",
237
+ :meetingName => "meeting-name", :maxUsers => "100", :voiceBridge => "12341234", :createTime => "123123123",
238
+ :recording => "false", :meta_1 => "abc", :meta_2 => "2" }
239
+ } # hash after the send_api_request call, before the formatting
240
+
241
+ let(:expected_attendee1) { { :userID => "123", :fullName => "Dexter Morgan", :role => :moderator } }
242
+ let(:expected_attendee2) { { :userID => "id2", :fullName => "Cameron", :role => :viewer } }
243
+ let(:final_response) {
244
+ { :meetingID => "123", :moderatorPW => "111", :attendeePW => "222", :hasBeenForciblyEnded => false,
245
+ :running => true, :startTime => DateTime.parse("Thu Sep 01 17:51:42 UTC 2011"), :endTime => nil,
246
+ :returncode => true, :attendees => [ expected_attendee1, expected_attendee2 ],
247
+ :messageKey => "mkey", :message => "m", :participantCount => 50, :moderatorCount => 3,
248
+ :meetingName => "meeting-name", :maxUsers => 100, :voiceBridge => 12341234, :createTime => 123123123,
249
+ :recording => false, :meta_1 => "abc", :meta_2 => "2" }
250
+ } # expected return hash after all the formatting
149
251
 
150
- before { api.should_receive(:send_api_request).with(:join, params).and_return("join-return") }
151
- it {
152
- options = { :userID => "id123", :webVoiceConf => 12345678 }
153
- api.join_meeting("meeting-id", "Name", "pw", options).should == "join-return"
154
- }
252
+ # ps: not mocking the formatter here because it's easier to just check the results (final_response)
253
+ before { api.should_receive(:send_api_request).with(:getMeetingInfo, params).and_return(response) }
254
+ it { api.get_meeting_info(meeting_id, password).should == final_response }
155
255
  end
156
256
 
157
257
  context "accepts non standard options" do
158
- let(:params) {
159
- { :meetingID => "meeting-id", :password => "pw",
160
- :fullName => "Name", :userID => "id123", :nonStandard => 1 }
258
+ let(:params_in) {
259
+ { :anything1 => "anything-1", :anything2 => 2 }
260
+ }
261
+ let(:params_out) {
262
+ { :meetingID => meeting_id, :password => password,
263
+ :anything1 => "anything-1", :anything2 => 2 }
161
264
  }
162
- before { api.should_receive(:send_api_request).with(:join, params) }
163
- it { api.join_meeting("meeting-id", "Name", "pw", params) }
265
+ before { api.should_receive(:send_api_request).with(:getMeetingInfo, params_out).and_return({}) }
266
+ it { api.get_meeting_info(meeting_id, password, params_in) }
164
267
  end
165
268
  end
166
269
 
167
- describe "#get_meeting_info" do
168
- let(:meeting_id) { "meeting-id" }
169
- let(:password) { "password" }
170
- let(:params) { { :meetingID => meeting_id, :password => password } }
171
-
172
- let(:attendee1) { { :userID => 123, :fullName => "Dexter Morgan", :role => "MODERATOR" } }
173
- let(:attendee2) { { :userID => "id2", :fullName => "Cameron", :role => "VIEWER" } }
174
- let(:response) {
175
- { :meetingID => 123, :moderatorPW => 111, :attendeePW => 222, :hasBeenForciblyEnded => "FALSE",
176
- :running => "TRUE", :startTime => "Thu Sep 01 17:51:42 UTC 2011", :endTime => "null",
177
- :returncode => true, :attendees => { :attendee => [ attendee1, attendee2 ] },
178
- :messageKey => "mkey", :message => "m", :participantCount => "50", :moderatorCount => "3" }
179
- } # hash after the send_api_request call, before the formatting
180
-
181
- let(:expected_attendee1) { { :userID => "123", :fullName => "Dexter Morgan", :role => :moderator } }
182
- let(:expected_attendee2) { { :userID => "id2", :fullName => "Cameron", :role => :viewer } }
183
- let(:final_response) {
184
- { :meetingID => "123", :moderatorPW => "111", :attendeePW => "222", :hasBeenForciblyEnded => false,
185
- :running => true, :startTime => DateTime.parse("Thu Sep 01 17:51:42 UTC 2011"), :endTime => nil,
186
- :returncode => true, :attendees => [ expected_attendee1, expected_attendee2 ],
187
- :messageKey => "mkey", :message => "m", :participantCount => 50, :moderatorCount => 3 }
188
- } # expected return hash after all the formatting
189
-
190
- # ps: not mocking the formatter here because it's easier to just check the results (final_response)
191
- before { api.should_receive(:send_api_request).with(:getMeetingInfo, params).and_return(response) }
192
- it { api.get_meeting_info(meeting_id, password).should == final_response }
193
- end
194
-
195
270
  describe "#get_meetings" do
196
- let(:meeting_hash1) { { :meetingID => "Demo Meeting", :attendeePW => "ap", :moderatorPW => "mp", :hasBeenForciblyEnded => false, :running => true } }
197
- let(:meeting_hash2) { { :meetingID => "Ended Meeting", :attendeePW => "pass", :moderatorPW => "pass", :hasBeenForciblyEnded => true, :running => false } }
198
- let(:flattened_response) {
199
- { :returncode => true, :meetings => [ meeting_hash1, meeting_hash2 ], :messageKey => "mkey", :message => "m" }
200
- } # hash *after* the flatten_objects call
271
+ context "standard case" do
272
+ let(:meeting_hash1) { { :meetingID => "Demo Meeting", :attendeePW => "ap", :moderatorPW => "mp", :hasBeenForciblyEnded => false, :running => true } }
273
+ let(:meeting_hash2) { { :meetingID => "Ended Meeting", :attendeePW => "pass", :moderatorPW => "pass", :hasBeenForciblyEnded => true, :running => false } }
274
+ let(:flattened_response) {
275
+ { :returncode => true, :meetings => [ meeting_hash1, meeting_hash2 ], :messageKey => "mkey", :message => "m" }
276
+ } # hash *after* the flatten_objects call
201
277
 
202
- before {
203
- api.should_receive(:send_api_request).with(:getMeetings, hash_including(:random => kind_of(Integer))).
278
+ before {
279
+ api.should_receive(:send_api_request).with(:getMeetings, {}).
204
280
  and_return(flattened_response)
205
- formatter_mock = mock(BigBlueButton::BigBlueButtonFormatter)
206
- formatter_mock.should_receive(:flatten_objects).with(:meetings, :meeting)
207
- BigBlueButton::BigBlueButtonFormatter.should_receive(:new).and_return(formatter_mock)
208
- BigBlueButton::BigBlueButtonFormatter.should_receive(:format_meeting).with(meeting_hash1)
209
- BigBlueButton::BigBlueButtonFormatter.should_receive(:format_meeting).with(meeting_hash2)
210
- }
211
- it { api.get_meetings }
281
+ formatter_mock = mock(BigBlueButton::BigBlueButtonFormatter)
282
+ formatter_mock.should_receive(:flatten_objects).with(:meetings, :meeting)
283
+ BigBlueButton::BigBlueButtonFormatter.should_receive(:new).and_return(formatter_mock)
284
+ BigBlueButton::BigBlueButtonFormatter.should_receive(:format_meeting).with(meeting_hash1)
285
+ BigBlueButton::BigBlueButtonFormatter.should_receive(:format_meeting).with(meeting_hash2)
286
+ }
287
+ it { api.get_meetings }
288
+ end
289
+
290
+ context "accepts non standard options" do
291
+ let(:params) {
292
+ { :anything1 => "anything-1", :anything2 => 2 }
293
+ }
294
+ before { api.should_receive(:send_api_request).with(:getMeetings, params).and_return({}) }
295
+ it { api.get_meetings(params) }
296
+ end
212
297
  end
213
298
 
214
299
  describe "#get_api_version" do
215
300
  context "returns the version returned by the server" do
216
- let(:hash) { { :returncode => true, :version => "0.7" } }
301
+ let(:hash) { { :returncode => true, :version => "0.8" } }
217
302
  before { api.should_receive(:send_api_request).with(:index).and_return(hash) }
218
- it { api.get_api_version.should == "0.7" }
303
+ it { api.get_api_version.should == "0.8" }
219
304
  end
220
305
 
221
306
  context "returns an empty string when the server responds with an empty hash" do
@@ -370,12 +455,12 @@ describe BigBlueButton::BigBlueButtonApi do
370
455
  end
371
456
 
372
457
  context "checking if it has a :response key" do
373
- before { Hash.should_receive(:from_xml).with("response-body").and_return({ }) }
458
+ before { BigBlueButton::BigBlueButtonHash.should_receive(:from_xml).with("response-body").and_return({ }) }
374
459
  it { expect { make_request }.to raise_error(BigBlueButton::BigBlueButtonException) }
375
460
  end
376
461
 
377
462
  context "checking if it the :response key has a :returncode key" do
378
- before { Hash.should_receive(:from_xml).with("response-body").and_return({ :response => { } }) }
463
+ before { BigBlueButton::BigBlueButtonHash.should_receive(:from_xml).with("response-body").and_return({ :response => { } }) }
379
464
  it { expect { make_request }.to raise_error(BigBlueButton::BigBlueButtonException) }
380
465
  end
381
466
  end
@@ -386,7 +471,7 @@ describe BigBlueButton::BigBlueButtonApi do
386
471
  before do
387
472
  api.should_receive(:send_request).with(url, data).and_return(response_mock)
388
473
  response_mock.should_receive(:body).twice.and_return("response-body")
389
- Hash.should_receive(:from_xml).with("response-body").and_return(response)
474
+ BigBlueButton::BigBlueButtonHash.should_receive(:from_xml).with("response-body").and_return(response)
390
475
 
391
476
  # here starts the validation
392
477
  # doesn't test the resulting format, only that the formatter was called
@@ -403,7 +488,7 @@ describe BigBlueButton::BigBlueButtonApi do
403
488
  before do
404
489
  api.should_receive(:send_request).with(url, data).and_return(response_mock)
405
490
  response_mock.should_receive(:body).twice.and_return("response-body")
406
- Hash.should_receive(:from_xml).with("response-body").and_return(response)
491
+ BigBlueButton::BigBlueButtonHash.should_receive(:from_xml).with("response-body").and_return(response)
407
492
 
408
493
  formatter_mock = mock(BigBlueButton::BigBlueButtonFormatter)
409
494
  BigBlueButton::BigBlueButtonFormatter.should_receive(:new).with(response).and_return(formatter_mock)
@@ -473,7 +558,174 @@ describe BigBlueButton::BigBlueButtonApi do
473
558
  api.send(:send_request, url, data).should == "ok"
474
559
  }
475
560
  end
561
+ end
562
+
563
+ describe "#get_recordings" do
564
+ let(:recording1) { { :recordID => "id1", :meetindID => "meeting-id" } } # simplified "recording" node in the response
565
+ let(:recording2) { { :recordID => "id2", :meetindID => "meeting-id" } }
566
+ let(:response) {
567
+ { :returncode => true, :recordings => { :recording => [ recording1, recording2 ] }, :messageKey => "mkey", :message => "m" }
568
+ }
569
+ let(:flattened_response) {
570
+ { :returncode => true, :recordings => [ recording1, recording2 ], :messageKey => "mkey", :message => "m" }
571
+ } # hash *after* the flatten_objects call
572
+
573
+ context "accepts non standard options" do
574
+ let(:params) { { :meetingID => "meeting-id", :nonStandard => 1 } }
575
+ before { api.should_receive(:send_api_request).with(:getRecordings, params).and_return(response) }
576
+ it { api.get_recordings(params) }
577
+ end
578
+
579
+ context "without meeting ID" do
580
+ before { api.should_receive(:send_api_request).with(:getRecordings, {}).and_return(response) }
581
+ it { api.get_recordings.should == response }
582
+ end
583
+
584
+ context "with one meeting ID" do
585
+ context "in an array" do
586
+ let(:options) { { :meetingID => ["meeting-id"] } }
587
+ let(:req_params) { { :meetingID => "meeting-id" } }
588
+ before { api.should_receive(:send_api_request).with(:getRecordings, req_params).and_return(response) }
589
+ it { api.get_recordings(options).should == response }
590
+ end
591
+
592
+ context "in a string" do
593
+ let(:options) { { :meetingID => "meeting-id" } }
594
+ let(:req_params) { { :meetingID => "meeting-id" } }
595
+ before { api.should_receive(:send_api_request).with(:getRecordings, req_params).and_return(response) }
596
+ it { api.get_recordings(options).should == response }
597
+ end
598
+ end
599
+
600
+ context "with several meeting IDs" do
601
+ context "in an array" do
602
+ let(:options) { { :meetingID => ["meeting-id-1", "meeting-id-2"] } }
603
+ let(:req_params) { { :meetingID => "meeting-id-1,meeting-id-2" } }
604
+ before { api.should_receive(:send_api_request).with(:getRecordings, req_params).and_return(response) }
605
+ it { api.get_recordings(options).should == response }
606
+ end
607
+
608
+ context "in a string" do
609
+ let(:options) { { :meetingID => "meeting-id-1,meeting-id-2" } }
610
+ let(:req_params) { { :meetingID => "meeting-id-1,meeting-id-2" } }
611
+ before { api.should_receive(:send_api_request).with(:getRecordings, req_params).and_return(response) }
612
+ it { api.get_recordings(options).should == response }
613
+ end
614
+ end
615
+
616
+ context "formats the response" do
617
+ before {
618
+ api.should_receive(:send_api_request).with(:getRecordings, anything).and_return(flattened_response)
619
+ formatter_mock = mock(BigBlueButton::BigBlueButtonFormatter)
620
+ formatter_mock.should_receive(:flatten_objects).with(:recordings, :recording)
621
+ BigBlueButton::BigBlueButtonFormatter.should_receive(:format_recording).with(recording1)
622
+ BigBlueButton::BigBlueButtonFormatter.should_receive(:format_recording).with(recording2)
623
+ BigBlueButton::BigBlueButtonFormatter.should_receive(:new).and_return(formatter_mock)
624
+ }
625
+ it { api.get_recordings }
626
+ end
627
+ end
628
+
629
+ describe "#publish_recordings" do
630
+
631
+ context "publish is converted to string" do
632
+ let(:recordIDs) { "any" }
633
+ let(:req_params) { { :publish => "false", :recordID => "any" } }
634
+ before { api.should_receive(:send_api_request).with(:publishRecordings, req_params) }
635
+ it { api.publish_recordings(recordIDs, false) }
636
+ end
637
+
638
+ context "with one recording ID" do
639
+ context "in an array" do
640
+ let(:recordIDs) { ["id-1"] }
641
+ let(:req_params) { { :publish => "true", :recordID => "id-1" } }
642
+ before { api.should_receive(:send_api_request).with(:publishRecordings, req_params) }
643
+ it { api.publish_recordings(recordIDs, true) }
644
+ end
645
+
646
+ context "in a string" do
647
+ let(:recordIDs) { "id-1" }
648
+ let(:req_params) { { :publish => "true", :recordID => "id-1" } }
649
+ before { api.should_receive(:send_api_request).with(:publishRecordings, req_params) }
650
+ it { api.publish_recordings(recordIDs, true) }
651
+ end
652
+ end
476
653
 
654
+ context "with several recording IDs" do
655
+ context "in an array" do
656
+ let(:recordIDs) { ["id-1", "id-2"] }
657
+ let(:req_params) { { :publish => "true", :recordID => "id-1,id-2" } }
658
+ before { api.should_receive(:send_api_request).with(:publishRecordings, req_params) }
659
+ it { api.publish_recordings(recordIDs, true) }
660
+ end
661
+
662
+ context "in a string" do
663
+ let(:recordIDs) { "id-1,id-2,id-3" }
664
+ let(:req_params) { { :publish => "true", :recordID => "id-1,id-2,id-3" } }
665
+ before { api.should_receive(:send_api_request).with(:publishRecordings, req_params) }
666
+ it { api.publish_recordings(recordIDs, true) }
667
+ end
668
+ end
669
+
670
+ context "accepts non standard options" do
671
+ let(:recordIDs) { ["id-1"] }
672
+ let(:params_in) {
673
+ { :anything1 => "anything-1", :anything2 => 2 }
674
+ }
675
+ let(:params_out) {
676
+ { :publish => "true", :recordID => "id-1",
677
+ :anything1 => "anything-1", :anything2 => 2 }
678
+ }
679
+ before { api.should_receive(:send_api_request).with(:publishRecordings, params_out) }
680
+ it { api.publish_recordings(recordIDs, true, params_in) }
681
+ end
682
+ end
683
+
684
+ describe "#delete_recordings" do
685
+
686
+ context "with one recording ID" do
687
+ context "in an array" do
688
+ let(:recordIDs) { ["id-1"] }
689
+ let(:req_params) { { :recordID => "id-1" } }
690
+ before { api.should_receive(:send_api_request).with(:deleteRecordings, req_params) }
691
+ it { api.delete_recordings(recordIDs) }
692
+ end
693
+
694
+ context "in a string" do
695
+ let(:recordIDs) { "id-1" }
696
+ let(:req_params) { { :recordID => "id-1" } }
697
+ before { api.should_receive(:send_api_request).with(:deleteRecordings, req_params) }
698
+ it { api.delete_recordings(recordIDs) }
699
+ end
700
+ end
701
+
702
+ context "with several recording IDs" do
703
+ context "in an array" do
704
+ let(:recordIDs) { ["id-1", "id-2"] }
705
+ let(:req_params) { { :recordID => "id-1,id-2" } }
706
+ before { api.should_receive(:send_api_request).with(:deleteRecordings, req_params) }
707
+ it { api.delete_recordings(recordIDs) }
708
+ end
709
+
710
+ context "in a string" do
711
+ let(:recordIDs) { "id-1,id-2,id-3" }
712
+ let(:req_params) { { :recordID => "id-1,id-2,id-3" } }
713
+ before { api.should_receive(:send_api_request).with(:deleteRecordings, req_params) }
714
+ it { api.delete_recordings(recordIDs) }
715
+ end
716
+ end
717
+
718
+ context "accepts non standard options" do
719
+ let(:recordIDs) { ["id-1"] }
720
+ let(:params_in) {
721
+ { :anything1 => "anything-1", :anything2 => 2 }
722
+ }
723
+ let(:params_out) {
724
+ { :recordID => "id-1", :anything1 => "anything-1", :anything2 => 2 }
725
+ }
726
+ before { api.should_receive(:send_api_request).with(:deleteRecordings, params_out) }
727
+ it { api.delete_recordings(recordIDs, params_in) }
728
+ end
477
729
  end
478
730
 
479
731
  end