bigbluebutton-api-ruby 0.0.11 → 0.1.0.rc1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (51) hide show
  1. data/.gitignore +5 -2
  2. data/.rvmrc +1 -1
  3. data/.travis.yml +6 -0
  4. data/CHANGELOG.rdoc +19 -8
  5. data/Gemfile +9 -1
  6. data/Gemfile.lock +62 -9
  7. data/LICENSE +5 -7
  8. data/LICENSE_003 +20 -0
  9. data/README.rdoc +42 -19
  10. data/Rakefile +31 -19
  11. data/bigbluebutton-api-ruby.gemspec +5 -5
  12. data/examples/get_version_example.rb +18 -0
  13. data/examples/join_example.rb +59 -0
  14. data/examples/overall_0.7_example.rb +92 -0
  15. data/examples/prepare.rb +38 -0
  16. data/extras/bigbluebutton_bot.rb +64 -0
  17. data/extras/download_bot_from.txt +1 -0
  18. data/extras/test-presentation.pdf +0 -0
  19. data/features/check_status.feature +45 -0
  20. data/features/config.yml.example +21 -0
  21. data/features/create_meetings.feature +29 -0
  22. data/features/end_meetings.feature +27 -0
  23. data/features/join_meetings.feature +29 -0
  24. data/features/pre_upload_slides.feature +14 -0
  25. data/features/recordings.feature +34 -0
  26. data/features/step_definitions/check_status_steps.rb +119 -0
  27. data/features/step_definitions/common_steps.rb +122 -0
  28. data/features/step_definitions/create_meetings_steps.rb +54 -0
  29. data/features/step_definitions/end_meetings_steps.rb +49 -0
  30. data/features/step_definitions/join_meetings_steps.rb +39 -0
  31. data/features/step_definitions/pre_upload_slides_steps.rb +13 -0
  32. data/features/step_definitions/recordings_steps.rb +38 -0
  33. data/features/support/api_tests/configs.rb +51 -0
  34. data/features/support/env.rb +7 -0
  35. data/features/support/hooks.rb +11 -0
  36. data/lib/bigbluebutton_api.rb +301 -97
  37. data/lib/bigbluebutton_formatter.rb +105 -19
  38. data/lib/bigbluebutton_modules.rb +92 -0
  39. data/lib/hash_to_xml.rb +22 -51
  40. data/spec/bigbluebutton_api_0.8_spec.rb +273 -0
  41. data/spec/bigbluebutton_api_spec.rb +211 -117
  42. data/spec/bigbluebutton_formatter_spec.rb +178 -29
  43. data/spec/bigbluebutton_modules_spec.rb +95 -0
  44. data/spec/data/hash_to_xml_complex.xml +45 -0
  45. data/spec/hash_to_xml_spec.rb +143 -0
  46. data/spec/spec_helper.rb +4 -2
  47. data/spec/support/forgery/forgeries/random_name.rb +7 -0
  48. data/spec/support/forgery/forgeries/url.rb +5 -0
  49. metadata +47 -12
  50. data/test/config.yml.example +0 -9
  51. data/test/test.rb +0 -154
@@ -5,12 +5,25 @@ module BigBlueButton
5
5
  attr_accessor :hash
6
6
 
7
7
  def initialize(hash)
8
- @hash = hash
8
+ @hash = hash || {}
9
9
  end
10
10
 
11
11
  # converts a value in the @hash to boolean
12
12
  def to_boolean(key)
13
- @hash[key] = @hash[key].downcase == "true"
13
+ unless @hash.has_key?(key)
14
+ false
15
+ else
16
+ @hash[key] = @hash[key].downcase == "true"
17
+ end
18
+ end
19
+
20
+ # converts a value in the @hash to int
21
+ def to_int(key)
22
+ unless @hash.has_key?(key)
23
+ 0
24
+ else
25
+ @hash[key] = @hash[key].to_i
26
+ end
14
27
  end
15
28
 
16
29
  # converts a value in the @hash to string
@@ -20,14 +33,51 @@ module BigBlueButton
20
33
 
21
34
  # converts a value in the @hash to DateTime
22
35
  def to_datetime(key)
23
- @hash[key] = @hash[key].downcase == "null" ? nil : DateTime.parse(@hash[key])
36
+ unless @hash.has_key?(key) and @hash[key]
37
+ nil
38
+ else
39
+ # BBB >= 0.8 uses the unix epoch for all time related values
40
+ # older versions use strings
41
+
42
+ # a number but in a String class
43
+ if (@hash[key].class == String && @hash[key].to_i.to_s == @hash[key])
44
+ value = @hash[key].to_i
45
+ else
46
+ value = @hash[key]
47
+ end
48
+
49
+ if value.is_a?(Numeric)
50
+ result = value == 0 ? nil : DateTime.parse(Time.at(value/1000.0).to_s)
51
+ else
52
+ if value.downcase == "null"
53
+ result = nil
54
+ else
55
+ result = DateTime.parse(value)
56
+ end
57
+ end
58
+
59
+ @hash[key] = result
60
+ end
61
+ end
62
+
63
+ # converts a value in the @hash to a symbol
64
+ def to_sym(key)
65
+ unless @hash.has_key?(key)
66
+ ""
67
+ else
68
+ if @hash[key].instance_of?(Symbol)
69
+ @hash[key]
70
+ elsif @hash[key].empty?
71
+ ""
72
+ else
73
+ @hash[key] = @hash[key].downcase.to_sym
74
+ end
75
+ end
24
76
  end
25
77
 
26
78
  # Default formatting for all responses given by a BBB server
27
79
  def default_formatting
28
-
29
- # remove the "response" node
30
- response = Hash[@hash[:response]].inject({}){|h,(k,v)| h[k] = v; h}
80
+ response = @hash
31
81
 
32
82
  # Adjust some values. There will always be a returncode, a message and a messageKey in the hash.
33
83
  response[:returncode] = response[:returncode].downcase == "success" # true instead of "SUCCESS"
@@ -37,25 +87,61 @@ module BigBlueButton
37
87
  @hash = response
38
88
  end
39
89
 
40
- # default formatting for a meeting hash
41
- def format_meeting(meeting)
42
- meeting[:meetingID] = meeting[:meetingID].to_s
43
- meeting[:moderatorPW] = meeting[:moderatorPW].to_s
44
- meeting[:attendeePW] = meeting[:attendeePW].to_s
45
- meeting[:hasBeenForciblyEnded] = meeting[:hasBeenForciblyEnded].downcase == "true"
46
- meeting[:running] = meeting[:running].downcase == "true"
90
+ # Default formatting for a meeting hash
91
+ def self.format_meeting(meeting)
92
+ f = BigBlueButtonFormatter.new(meeting)
93
+ f.to_string(:meetingID)
94
+ f.to_string(:moderatorPW)
95
+ f.to_string(:attendeePW)
96
+ f.to_boolean(:hasBeenForciblyEnded)
97
+ f.to_boolean(:running)
98
+ f.to_int(:createTime) if meeting.has_key?(:createTime)
47
99
  meeting
48
100
  end
49
101
 
50
- # default formatting for an attendee hash
51
- def format_attendee(attendee)
52
- attendee[:userID] = attendee[:userID].to_s
53
- attendee[:role] = attendee[:role].downcase.to_sym
102
+ # Default formatting for an attendee hash
103
+ def self.format_attendee(attendee)
104
+ f = BigBlueButtonFormatter.new(attendee)
105
+ f.to_string(:userID)
106
+ f.to_sym(:role)
54
107
  attendee
55
108
  end
56
109
 
57
- # simplifies the hash making a node e.g. :attendee with an array with all attendees
58
- # TODO: comments with the expected @hash at this point
110
+ # Default formatting for a recording hash
111
+ def self.format_recording(rec)
112
+ f = BigBlueButtonFormatter.new(rec)
113
+ f.to_string(:recordID)
114
+ f.to_string(:meetingID)
115
+ f.to_string(:name)
116
+ f.to_boolean(:published)
117
+ f.to_datetime(:startTime)
118
+ f.to_datetime(:endTime)
119
+ rec
120
+ end
121
+
122
+ # Simplifies the XML-styled hash node 'first'. Its value will then always be an Array.
123
+ #
124
+ # For example, if the current hash is:
125
+ # { :name => "Test", :attendees => { :attendee => [ { :name => "attendee1" }, { :name => "attendee2" } ] } }
126
+ #
127
+ # Calling:
128
+ # flatten_objects(:attendees, :attendee)
129
+ #
130
+ # The hash will become:
131
+ # { :name => "Test", :attendees => [ { :name => "attendee1" }, { :name => "attendee2" } ] }
132
+ #
133
+ # Other examples:
134
+ #
135
+ # Hash:
136
+ # { :name => "Test", :attendees => {} }
137
+ # Result:
138
+ # { :name => "Test", :attendees => [] }
139
+ #
140
+ # Hash:
141
+ # { :name => "Test", :attendees => { :attendee => { :name => "attendee1" } } }
142
+ # Result:
143
+ # { :name => "Test", :attendees => [ { :name => "attendee1" } ] }
144
+ #
59
145
  def flatten_objects(first, second)
60
146
  if @hash[first].empty?
61
147
  collection = []
@@ -0,0 +1,92 @@
1
+ require "base64"
2
+
3
+ module BigBlueButton
4
+
5
+ # An object to store the modules configuration to be passed in
6
+ # BigBlueButtonApi#create_meeting().
7
+ #
8
+ # === Usage example:
9
+ #
10
+ # modules = BigBlueButton::BigBlueButtonModules.new
11
+ #
12
+ # # adds presentations by URL
13
+ # modules.add_presentation(:url, "http://www.samplepdf.com/sample.pdf")
14
+ # modules.add_presentation(:url, "http://www.samplepdf.com/sample2.pdf")
15
+ #
16
+ # # adds presentations from a local file
17
+ # # the file will be opened and encoded in base64
18
+ # modules.add_presentation(:file, "presentations/class01.ppt")
19
+ #
20
+ # # adds a base64 encoded presentation
21
+ # modules.add_presentation(:base64, "JVBERi0xLjQKJ....[clipped here]....0CiUlRU9GCg==", "first-class.pdf")
22
+ #
23
+ class BigBlueButtonModules
24
+
25
+ attr_accessor :presentation_urls
26
+ attr_accessor :presentation_files
27
+ attr_accessor :presentation_base64s
28
+
29
+ def initialize
30
+ @presentation_urls = []
31
+ @presentation_files = []
32
+ @presentation_base64s = []
33
+ end
34
+
35
+ def add_presentation(type, value, name=nil)
36
+ case type
37
+ when :url
38
+ @presentation_urls.push(value)
39
+ when :file
40
+ @presentation_files.push(value)
41
+ when :base64
42
+ @presentation_base64s.push([name, value])
43
+ end
44
+ end
45
+
46
+ def to_xml
47
+ unless has_presentations?
48
+ ""
49
+ else
50
+ xml = xml_header
51
+ xml << presentations_to_xml
52
+ xml << xml_footer
53
+ end
54
+ end
55
+
56
+ private
57
+
58
+ def has_presentations?
59
+ !@presentation_urls.empty? or
60
+ !@presentation_files.empty? or
61
+ !@presentation_base64s.empty?
62
+ end
63
+
64
+ def xml_header
65
+ "<?xml version=\"1.0\" encoding=\"UTF-8\"?><modules>"
66
+ end
67
+
68
+ def xml_footer
69
+ "</modules>"
70
+ end
71
+
72
+ def presentations_to_xml
73
+ xml = "<module name=\"presentation\">"
74
+ @presentation_urls.each { |url| xml << "<document url=\"#{url}\" />" }
75
+ @presentation_base64s.each do |name, data|
76
+ xml << "<document name=\"#{name}\">"
77
+ xml << data
78
+ xml << "</document>"
79
+ end
80
+ @presentation_files.each do |filename|
81
+ xml << "<document name=\"#{File.basename(filename)}\">"
82
+ File.open(filename, "r") do |file|
83
+ xml << Base64.encode64(file.read)
84
+ end
85
+ xml << "</document>"
86
+ end
87
+ xml << "</module>"
88
+ end
89
+
90
+ end
91
+
92
+ end
data/lib/hash_to_xml.rb CHANGED
@@ -1,62 +1,33 @@
1
- # Code from: https://gist.github.com/335286
2
- # USAGE: Hash.from_xml:(YOUR_XML_STRING)
3
- # modified from http://stackoverflow.com/questions/1230741/convert-a-nokogiri-document-to-a-ruby-hash/1231297#1231297
1
+ require 'xmlsimple'
4
2
 
5
3
  class Hash
6
4
  class << self
7
- def from_xml(xml_io)
5
+ def from_xml(xml_io)
8
6
  begin
9
- result = Nokogiri::XML(xml_io)
10
- return { result.root.name.to_sym => xml_node_to_hash(result.root)}
7
+ # we'll not use 'KeyToSymbol' because it doesn't symbolize the keys for node attributes
8
+ opts = { 'ForceArray' => false, 'ForceContent' => false } #
9
+ hash = XmlSimple.xml_in(xml_io, opts)
10
+ return symbolize_keys(hash)
11
11
  rescue Exception => e
12
12
  raise BigBlueButton::BigBlueButtonException.new("Impossible to convert XML to hash. Error: #{e.message}")
13
13
  end
14
- end
15
-
16
- def xml_node_to_hash(node)
17
- # If we are at the root of the document, start the hash
18
- if node.element?
19
- result_hash = {}
20
- if node.attributes != {}
21
- result_hash[:attributes] = {}
22
- node.attributes.keys.each do |key|
23
- result_hash[:attributes][node.attributes[key].name.to_sym] = prepare(node.attributes[key].value)
24
- end
25
- end
26
- if node.children.size > 0
27
- node.children.each do |child|
28
- result = xml_node_to_hash(child)
29
-
30
- if child.name == "text"
31
- unless child.next_sibling || child.previous_sibling
32
- return prepare(result)
33
- end
34
- elsif result_hash[child.name.to_sym]
35
- if result_hash[child.name.to_sym].is_a?(Object::Array)
36
- result_hash[child.name.to_sym] << prepare(result)
37
- else
38
- result_hash[child.name.to_sym] = [result_hash[child.name.to_sym]] << prepare(result)
39
- end
40
- else
41
- result_hash[child.name.to_sym] = prepare(result)
42
- end
43
- end
44
-
45
- return result_hash
46
- else
47
- return result_hash
48
- end
49
- else
50
- return prepare(node.content.to_s)
51
- end
52
- end
14
+ end
53
15
 
54
- def prepare(data)
55
- (data.class == String && data.to_i.to_s == data) ? data.to_i : data
16
+ def symbolize_keys(arg)
17
+ case arg
18
+ when Array
19
+ arg.map { |elem| symbolize_keys elem }
20
+ when Hash
21
+ Hash[
22
+ arg.map { |key, value|
23
+ k = key.is_a?(String) ? key.to_sym : key
24
+ v = symbolize_keys value
25
+ [k,v]
26
+ }]
27
+ else
28
+ arg
29
+ end
56
30
  end
57
- end
58
-
59
- def to_struct(struct_name)
60
- Struct.new(struct_name,*keys).new(*values)
31
+
61
32
  end
62
33
  end
@@ -0,0 +1,273 @@
1
+ require 'spec_helper'
2
+
3
+ # Tests for BBB API version 0.8
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.8" }
10
+ let(:debug) { false }
11
+ let(:api) { BigBlueButton::BigBlueButtonApi.new(url, salt, version, debug) }
12
+
13
+ describe "#create_meeting" do
14
+
15
+ context "without modules" do
16
+ let(:req_params) {
17
+ { :name => "name", :meetingID => "meeting-id", :moderatorPW => "mp", :attendeePW => "ap",
18
+ :welcome => "Welcome!", :dialNumber => 12345678, :logoutURL => "http://example.com",
19
+ :maxParticipants => 25, :voiceBridge => 12345, :record => "true", :duration => 20,
20
+ :meta_1 => "meta1", :meta_2 => "meta2" }
21
+ }
22
+ let(:req_response) {
23
+ { :meetingID => 123, :moderatorPW => 111, :attendeePW => 222, :hasBeenForciblyEnded => "FALSE", :createTime => "123123123" }
24
+ }
25
+ let(:final_response) {
26
+ { :meetingID => "123", :moderatorPW => "111", :attendeePW => "222", :hasBeenForciblyEnded => false, :createTime => 123123123 }
27
+ }
28
+
29
+ before { api.should_receive(:send_api_request).with(:create, req_params).and_return(req_response) }
30
+ subject {
31
+ options = { :moderatorPW => "mp", :attendeePW => "ap", :welcome => "Welcome!", :dialNumber => 12345678,
32
+ :logoutURL => "http://example.com", :maxParticipants => 25, :voiceBridge => 12345, :record => true,
33
+ :duration => 20, :meta_1 => "meta1", :meta_2 => "meta2" }
34
+ api.create_meeting("name", "meeting-id", options)
35
+ }
36
+ it { subject.should == final_response }
37
+ end
38
+
39
+ context "with modules" do
40
+ let(:req_params) {
41
+ { :name => "name", :meetingID => "meeting-id", :moderatorPW => "mp", :attendeePW => "ap" }
42
+ }
43
+ let(:req_response) {
44
+ { :meetingID => 123, :moderatorPW => 111, :attendeePW => 222, :hasBeenForciblyEnded => "FALSE", :createTime => "123123123" }
45
+ }
46
+ let(:final_response) {
47
+ { :meetingID => "123", :moderatorPW => "111", :attendeePW => "222", :hasBeenForciblyEnded => false, :createTime => 123123123 }
48
+ }
49
+ let(:modules) {
50
+ m = BigBlueButton::BigBlueButtonModules.new
51
+ m.add_presentation(:url, "http://www.samplepdf.com/sample.pdf")
52
+ m.add_presentation(:url, "http://www.samplepdf.com/sample2.pdf")
53
+ m.add_presentation(:base64, "JVBERi0xLjQKJ....[clipped here]....0CiUlRU9GCg==", "first-class.pdf")
54
+ m
55
+ }
56
+
57
+ before {
58
+ api.should_receive(:send_api_request).with(:create, req_params, modules.to_xml).
59
+ and_return(req_response)
60
+ }
61
+ subject {
62
+ options = { :moderatorPW => "mp", :attendeePW => "ap" }
63
+ api.create_meeting("name", "meeting-id", options, modules)
64
+ }
65
+ it { subject.should == final_response }
66
+ end
67
+ end
68
+
69
+ describe "#join_meeting_url" do
70
+ let(:params) {
71
+ { :meetingID => "meeting-id", :password => "pw", :fullName => "Name",
72
+ :userID => "id123", :webVoiceConf => 12345678, :createTime => 9876543 }
73
+ }
74
+
75
+ before { api.should_receive(:get_url).with(:join, params).and_return("test-url") }
76
+ it {
77
+ options = { :userID => "id123", :webVoiceConf => 12345678, :createTime => 9876543 }
78
+ api.join_meeting_url("meeting-id", "Name", "pw", options).should == "test-url"
79
+ }
80
+ end
81
+
82
+ describe "#join_meeting" do
83
+ let(:params) {
84
+ { :meetingID => "meeting-id", :password => "pw", :fullName => "Name",
85
+ :userID => "id123", :webVoiceConf => 12345678, :createTime => 9876543 }
86
+ }
87
+
88
+ before { api.should_receive(:send_api_request).with(:join, params).and_return("join-return") }
89
+ it {
90
+ options = { :userID => "id123", :webVoiceConf => 12345678, :createTime => 9876543 }
91
+ api.join_meeting("meeting-id", "Name", "pw", options).should == "join-return"
92
+ }
93
+ end
94
+
95
+ describe "#get_recordings" do
96
+ let(:recording1) { { :recordID => "id1", :meetindID => "meeting-id" } } # simplified "recording" node in the response
97
+ let(:recording2) { { :recordID => "id2", :meetindID => "meeting-id" } }
98
+ let(:response) {
99
+ { :returncode => true, :recordings => { :recording => [ recording1, recording2 ] }, :messageKey => "mkey", :message => "m" }
100
+ }
101
+ let(:flattened_response) {
102
+ { :returncode => true, :recordings => [ recording1, recording2 ], :messageKey => "mkey", :message => "m" }
103
+ } # hash *after* the flatten_objects call
104
+
105
+ context "only supported for >= 0.8" do
106
+ let(:api) { BigBlueButton::BigBlueButtonApi.new(url, salt, "0.7", debug) }
107
+ it { expect { api.get_recordings }.to raise_error(BigBlueButton::BigBlueButtonException) }
108
+ end
109
+
110
+ context "discards invalid options" do
111
+ let(:req_params) { { :meetingID => "meeting-id" } }
112
+ before { api.should_receive(:send_api_request).with(:getRecordings, req_params).and_return(response) }
113
+ it { api.get_recordings({ :meetingID => "meeting-id", :invalidParam1 => "1" }) }
114
+ end
115
+
116
+ context "without meeting ID" do
117
+ before { api.should_receive(:send_api_request).with(:getRecordings, {}).and_return(response) }
118
+ it { api.get_recordings.should == response }
119
+ end
120
+
121
+ context "with one meeting ID" do
122
+ context "in an array" do
123
+ let(:options) { { :meetingID => ["meeting-id"] } }
124
+ let(:req_params) { { :meetingID => "meeting-id" } }
125
+ before { api.should_receive(:send_api_request).with(:getRecordings, req_params).and_return(response) }
126
+ it { api.get_recordings(options).should == response }
127
+ end
128
+
129
+ context "in a string" do
130
+ let(:options) { { :meetingID => "meeting-id" } }
131
+ let(:req_params) { { :meetingID => "meeting-id" } }
132
+ before { api.should_receive(:send_api_request).with(:getRecordings, req_params).and_return(response) }
133
+ it { api.get_recordings(options).should == response }
134
+ end
135
+ end
136
+
137
+ context "with several meeting IDs" do
138
+ context "in an array" do
139
+ let(:options) { { :meetingID => ["meeting-id-1", "meeting-id-2"] } }
140
+ let(:req_params) { { :meetingID => "meeting-id-1,meeting-id-2" } }
141
+ before { api.should_receive(:send_api_request).with(:getRecordings, req_params).and_return(response) }
142
+ it { api.get_recordings(options).should == response }
143
+ end
144
+
145
+ context "in a string" do
146
+ let(:options) { { :meetingID => "meeting-id-1,meeting-id-2" } }
147
+ let(:req_params) { { :meetingID => "meeting-id-1,meeting-id-2" } }
148
+ before { api.should_receive(:send_api_request).with(:getRecordings, req_params).and_return(response) }
149
+ it { api.get_recordings(options).should == response }
150
+ end
151
+ end
152
+
153
+ context "formats the response" do
154
+ before {
155
+ api.should_receive(:send_api_request).with(:getRecordings, anything).and_return(flattened_response)
156
+ formatter_mock = mock(BigBlueButton::BigBlueButtonFormatter)
157
+ formatter_mock.should_receive(:flatten_objects).with(:recordings, :recording)
158
+ BigBlueButton::BigBlueButtonFormatter.should_receive(:format_recording).with(recording1)
159
+ BigBlueButton::BigBlueButtonFormatter.should_receive(:format_recording).with(recording2)
160
+ BigBlueButton::BigBlueButtonFormatter.should_receive(:new).and_return(formatter_mock)
161
+ }
162
+ it { api.get_recordings }
163
+ end
164
+ end
165
+
166
+ describe "#publish_recordings" do
167
+ context "only supported for >= 0.8" do
168
+ let(:api) { BigBlueButton::BigBlueButtonApi.new(url, salt, "0.7", debug) }
169
+ it { expect { api.publish_recordings("id", true) }.to raise_error(BigBlueButton::BigBlueButtonException) }
170
+ end
171
+
172
+ context "publish is converted to string" do
173
+ let(:recordIDs) { "any" }
174
+ let(:req_params) { { :publish => "false", :recordID => "any" } }
175
+ before { api.should_receive(:send_api_request).with(:publishRecordings, req_params) }
176
+ it { api.publish_recordings(recordIDs, false) }
177
+ end
178
+
179
+ context "with one recording ID" do
180
+ context "in an array" do
181
+ let(:recordIDs) { ["id-1"] }
182
+ let(:req_params) { { :publish => "true", :recordID => "id-1" } }
183
+ before { api.should_receive(:send_api_request).with(:publishRecordings, req_params) }
184
+ it { api.publish_recordings(recordIDs, true) }
185
+ end
186
+
187
+ context "in a string" do
188
+ let(:recordIDs) { "id-1" }
189
+ let(:req_params) { { :publish => "true", :recordID => "id-1" } }
190
+ before { api.should_receive(:send_api_request).with(:publishRecordings, req_params) }
191
+ it { api.publish_recordings(recordIDs, true) }
192
+ end
193
+ end
194
+
195
+ context "with several recording IDs" do
196
+ context "in an array" do
197
+ let(:recordIDs) { ["id-1", "id-2"] }
198
+ let(:req_params) { { :publish => "true", :recordID => "id-1,id-2" } }
199
+ before { api.should_receive(:send_api_request).with(:publishRecordings, req_params) }
200
+ it { api.publish_recordings(recordIDs, true) }
201
+ end
202
+
203
+ context "in a string" do
204
+ let(:recordIDs) { "id-1,id-2,id-3" }
205
+ let(:req_params) { { :publish => "true", :recordID => "id-1,id-2,id-3" } }
206
+ before { api.should_receive(:send_api_request).with(:publishRecordings, req_params) }
207
+ it { api.publish_recordings(recordIDs, true) }
208
+ end
209
+ end
210
+ end
211
+
212
+ describe "#delete_recordings" do
213
+ context "only supported for >= 0.8" do
214
+ let(:api) { BigBlueButton::BigBlueButtonApi.new(url, salt, "0.7", debug) }
215
+ it { expect { api.delete_recordings("id") }.to raise_error(BigBlueButton::BigBlueButtonException) }
216
+ end
217
+
218
+ context "with one recording ID" do
219
+ context "in an array" do
220
+ let(:recordIDs) { ["id-1"] }
221
+ let(:req_params) { { :recordID => "id-1" } }
222
+ before { api.should_receive(:send_api_request).with(:deleteRecordings, req_params) }
223
+ it { api.delete_recordings(recordIDs) }
224
+ end
225
+
226
+ context "in a string" do
227
+ let(:recordIDs) { "id-1" }
228
+ let(:req_params) { { :recordID => "id-1" } }
229
+ before { api.should_receive(:send_api_request).with(:deleteRecordings, req_params) }
230
+ it { api.delete_recordings(recordIDs) }
231
+ end
232
+ end
233
+
234
+ context "with several recording IDs" do
235
+ context "in an array" do
236
+ let(:recordIDs) { ["id-1", "id-2"] }
237
+ let(:req_params) { { :recordID => "id-1,id-2" } }
238
+ before { api.should_receive(:send_api_request).with(:deleteRecordings, req_params) }
239
+ it { api.delete_recordings(recordIDs) }
240
+ end
241
+
242
+ context "in a string" do
243
+ let(:recordIDs) { "id-1,id-2,id-3" }
244
+ let(:req_params) { { :recordID => "id-1,id-2,id-3" } }
245
+ before { api.should_receive(:send_api_request).with(:deleteRecordings, req_params) }
246
+ it { api.delete_recordings(recordIDs) }
247
+ end
248
+ end
249
+ end
250
+
251
+ describe "#get_meeting_info" do
252
+ let(:params) { { :meetingID => "meeting-id", :password => "password" } }
253
+
254
+ # new values were added in the response in 0.8 (we'll only test these values):
255
+ # meetingName, participantCount, maxUsers, voiceBridge, recording, metadata
256
+ let(:response) {
257
+ { :meetingName => 123, :maxUsers => "100", :voiceBridge => "12341234",
258
+ :createTime => "123123123", :attendees => { :attendee => [ ] },
259
+ :messageKey => "mkey", :message => "m", :recording => "false", :meta_1 => "abc", :meta_2 => "2" }
260
+ }
261
+
262
+ before { api.should_receive(:send_api_request).with(:getMeetingInfo, params).and_return(response) }
263
+ subject { api.get_meeting_info("meeting-id", "password") }
264
+ it { subject[:meetingName].should == "123" }
265
+ it { subject[:maxUsers].should == 100 }
266
+ it { subject[:voiceBridge].should == 12341234 }
267
+ it { subject[:createTime].should == 123123123 }
268
+ it { subject[:recording].should == false }
269
+ it { subject[:meta_1].should == "abc" }
270
+ it { subject[:meta_2].should == "2" }
271
+ end
272
+
273
+ end