em-ucengine 0.1.1 → 0.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,27 @@
1
+ module EventMachine
2
+ module UCEngine
3
+ module Brick
4
+ # A simple class to allowing you to test your bricks
5
+ module Test
6
+ # Return the instance of the brick
7
+ def brick
8
+ return @b unless @b.nil?
9
+ @b = app.new
10
+ @b.start MiniTest::Mock.new
11
+ @b
12
+ end
13
+
14
+ # Trigger a U.C.Engine event into the brick
15
+ #
16
+ # @param [String] name
17
+ # @param [Hash] data
18
+ def trigger(name, data={})
19
+ event = data.merge "type" => name
20
+ brick.routes[name].each do |proc|
21
+ brick.instance_exec(event, &proc)
22
+ end unless brick.routes[name].nil?
23
+ end
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,268 @@
1
+ # -*- coding: utf-8 -*-
2
+ require "uri"
3
+ require "json"
4
+
5
+
6
+ module UCEngine
7
+ class Client
8
+
9
+ # Create a new U.C.Engine client
10
+ #
11
+ # @param [String] Host of the U.C.Engine instance
12
+ # @param [Number] Port of the U.C.Engine instance
13
+ # @param [String] Entry point of the API
14
+ # @param [String] Version of U.C.Engine API
15
+ def initialize(host="localhost", port=5280, api_root="/api", api_version="0.6")
16
+ @host = host
17
+ @port = port
18
+ @root = api_root
19
+ @version = api_version
20
+ end
21
+
22
+ # Format url to api
23
+ #
24
+ # @param [String] Path of the method
25
+ def url(path)
26
+ URI.escape("http://#{@host}:#{@port}#{@root}/#{@version}#{path}")
27
+ end
28
+
29
+ # Get server time
30
+ def time(&block)
31
+ Session.new(self, nil, nil).time &block
32
+ end
33
+
34
+ # Connect to U.C.Engine
35
+ #
36
+ # @param [String] User name
37
+ # @param [String] Password
38
+ # @param [Hash] Metadata of the user
39
+ def connect(user, password, metadata=nil, &block)
40
+ body = { "name" => user, "credential" => password }
41
+ body["metadata"] = metadata if metadata
42
+ req = post(url("/presence"), {}, body)
43
+ answer_connect(req, &block)
44
+ end
45
+
46
+ # Create a user
47
+ #
48
+ # @param [Hash] data
49
+ def create_user(data)
50
+ # FIXME: for now users can't be created with metadata.
51
+ answer post("/user", data), &block
52
+ end
53
+
54
+ # Session represent the U.C.Engine client with and sid and uid
55
+ # See #connect
56
+ class Session < Struct.new(:uce, :uid, :sid)
57
+ def url(*args)
58
+ uce.url(*args)
59
+ end
60
+
61
+ ### Time - http://docs.ucengine.org/api.html#time ###
62
+
63
+ # Get server time
64
+ def time(&block)
65
+ answer get(url("/time")), &block
66
+ end
67
+
68
+ ### Presence - http://docs.ucengine.org/api.html#authentication ###
69
+
70
+ # Get infos on the presence
71
+ #
72
+ # @param [String] Sid
73
+ def presence(sid, &block)
74
+ answer get(url("/presence/#{sid}")), &block
75
+ end
76
+
77
+ # Disconnect a user
78
+ #
79
+ # @param [String] Sid
80
+ def disconnect(sid, &block)
81
+ answer delete(url "/presence/#{sid}"), &block
82
+ end
83
+
84
+ ### Users - http://docs.ucengine.org/api.html#user ###
85
+
86
+ # List users
87
+ def users(&block)
88
+ answer get(url "/user"), &block
89
+ end
90
+
91
+ # Get user info
92
+ #
93
+ # @param [String] uid
94
+ def user(uid, &block)
95
+ answer get(url("/user/#{uid}")), &block
96
+ end
97
+
98
+ # Create user
99
+ #
100
+ # @param [Hash] data
101
+ def create_user(data, &block)
102
+ answer post(url("/user"), data), &block
103
+ end
104
+
105
+ # Update user
106
+ #
107
+ # @param [String] uid
108
+ # @param [Hash] data
109
+ def update_user(uid, data, &block)
110
+ answer put(url("/user/#{uid}"), data), &block
111
+ end
112
+
113
+ # Delete a user
114
+ #
115
+ # @param [String] uid
116
+ def delete_user(uid, &block)
117
+ answer delete(url("/user/#{uid}")), &block
118
+ end
119
+
120
+ # Check user ACL
121
+ #
122
+ # @param [String] uid
123
+ # @param [String] action
124
+ # @param [String] object
125
+ # @param [Hash] conditions
126
+ # @param [String] meeting name
127
+ def user_can(uid, action, object, conditions={}, location="", &block)
128
+ answer_bool get(url("/user/#{uid}/can/#{action}/#{object}/#{location}"), :conditions => conditions), &block
129
+ end
130
+
131
+ ### Meetings - http://docs.ucengine.org/api.html#meeting ###
132
+
133
+ # List meetings
134
+ #
135
+ def meetings(&block)
136
+ answer get(url "/meeting/"), &block
137
+ end
138
+
139
+ # Get meeting
140
+ #
141
+ # @param [String] meeting
142
+ def meeting(meeting, &block)
143
+ answer get(url "/meeting/#{meeting}"), &block
144
+ end
145
+
146
+ # Create a meeting
147
+ #
148
+ # @param [String] meeting name
149
+ # @param [Hash] metadata
150
+ def create_meeting(meeting, body={}, &block)
151
+ body.merge!(:name => meeting)
152
+ answer post(url("/meeting/"), body), &block
153
+ end
154
+
155
+ # Update a meeting
156
+ #
157
+ # @param [String] meeting name
158
+ # @param [Hash] metadata
159
+ def update_meeting(meeting, body={}, &block)
160
+ answer put(url "/meeting//#{meeting}", body), &block
161
+ end
162
+
163
+ ### Rosters - http://docs.ucengine.org/api.html#join-a-meeting ###
164
+
165
+ # List users on the meeting
166
+ #
167
+ # @param [String] meeting
168
+ def roster(meeting, &block)
169
+ answer get(url "/meeting/#{meeting}/roster"), &block
170
+ end
171
+
172
+ # Join the meeting
173
+ #
174
+ # @param [String] meeting
175
+ def join_roster(meeting, body={}, &block)
176
+ answer post(url "/meeting/#{meeting}/roster", body), &block
177
+ end
178
+
179
+ # Quit the meeting
180
+ #
181
+ # @param [String] meeting
182
+ # @param [String] uid
183
+ def quit_roster(meeting, uid=nil, &block)
184
+ answer delete(url "/meeting/#{meeting}/roster/#{uid || @uid}"), &block
185
+ end
186
+
187
+ ### Events - http://docs.ucengine.org/api.html#events ###
188
+
189
+ # Get events
190
+ #
191
+ # @param [String] meeting
192
+ # @param [Hash] params
193
+ def events(meeting=nil, params={}, &block)
194
+ answer get(url("/event/#{meeting}"), params), &block
195
+ end
196
+
197
+ # Publish event
198
+ #
199
+ # @param [String] type
200
+ # @param [String] meeting
201
+ # @param [Hash] metadata
202
+ def publish(type, meeting=nil, metadata=nil, parent=nil, &block)
203
+ args = { :type => type, :uid => uid, :sid => sid }
204
+ args[:parent] = parent if parent
205
+ args[:metadata] = metadata if metadata
206
+ answer json_post(url("/event/#{meeting}"), args), &block
207
+ end
208
+
209
+ # Get event
210
+ #
211
+ # @param [String] id
212
+ def event(id, &block)
213
+ # Fixme: remove meeting fake param on the 0.7 release
214
+ answer get(url("/event/meeting/#{id}"), {}), &block
215
+ end
216
+
217
+ # Search
218
+ #
219
+ # @param [Hash] params
220
+ def search(params, &block)
221
+ answer get(url "/search/event/", params, &block)
222
+ end
223
+
224
+ ### Files - http://docs.ucengine.org/api.html#upload-a-file ###
225
+
226
+ # Delete a file
227
+ #
228
+ # @param [String] meeting
229
+ # @param [String] filename
230
+ def delete_file(meeting, filename, &block)
231
+ answer delete(url("/file/#{meeting}/#{filename}")), &block
232
+ end
233
+
234
+ # List files on a meeting room
235
+ #
236
+ # @param [String] meeting
237
+ # @param [Hash] params
238
+ def files(meeting, params={}, &block)
239
+ answer get(url("/file/#{meeting}"), params), &block
240
+ end
241
+
242
+ ### Roles - http://docs.ucengine.org/api.html#roles ###
243
+
244
+ # Create a role
245
+ #
246
+ # @param [Hash] data
247
+ def create_role(data, &block)
248
+ answer post(url("/role/"), data), &block
249
+ end
250
+
251
+ # Delete a role
252
+ #
253
+ # @param [String] name
254
+ def delete_role(name, &block)
255
+ answer delete(url "/role/#{name}"), &block
256
+ end
257
+
258
+ # Set a role to a user
259
+ #
260
+ # @param [String] uid
261
+ # @param [Hash] params
262
+ def user_role(uid, params={}, &block)
263
+ answer post(url("/user/#{uid}/roles"), params), &block
264
+ end
265
+ end
266
+ end
267
+ end
268
+
@@ -0,0 +1,239 @@
1
+ require "eventmachine"
2
+ require "em-http-request"
3
+ require "em-http/middleware/json_response"
4
+ require "multipart_body"
5
+ require "em-eventsource"
6
+
7
+ require_relative "errors"
8
+
9
+ module EventMachine
10
+ module UCEngine
11
+ # Use response as block
12
+ module EventMachineResponse
13
+ def answer(req, &block)
14
+ response = EM::DefaultDeferrable.new
15
+ req.errback do
16
+ error = UCEngine::Client::HttpError.new(0, "connect error", req.last_effective_url)
17
+ response.fail error
18
+ yield error, nil if block_given?
19
+ end
20
+ req.callback do
21
+ data = req.response
22
+ if data["error"]
23
+ error = UCEngine::Client::UCError.new(req.response_header.status, data["error"], req.last_effective_url)
24
+ response.fail error
25
+ yield error, nil if block_given?
26
+ else
27
+ response.succeed data["result"]
28
+ yield nil, data["result"] if block_given?
29
+ end
30
+ end
31
+ response
32
+ end
33
+
34
+ def answer_bool(req, &block)
35
+ answer(req) do |err, result|
36
+ result = (result == "true")
37
+ yield nil, result if block_given?
38
+ end
39
+ end
40
+
41
+ def answer_download(req)
42
+ response = EM::DefaultDeferrable.new
43
+ req.errback do
44
+ error = UCEngine::Client::HttpError.new(0, "connect error", req.last_effective_url)
45
+ response.fail error
46
+ yield error, nil if block_given?
47
+ end
48
+ req.callback do
49
+ if req.response_header.status == 200
50
+ data = req.response
51
+ filename = req.response_header['CONTENT_DISPOSITION']
52
+ file = Tempfile.new(filename)
53
+ file.write(data)
54
+ response.succeed file
55
+ yield nil, file if block_given?
56
+ else
57
+ error = UCEngine::Client::HttpError.new(0, "download error")
58
+ response.fail error
59
+ yield error, nil if block_given?
60
+ end
61
+ end
62
+ response
63
+ end
64
+
65
+ def answer_connect(http)
66
+ response = EM::DefaultDeferrable.new
67
+ http.errback do
68
+ error = UCEngine::Client::HttpError.new(0, "connect error", http.uri)
69
+ response.fail error
70
+ yield error, nil if block_given?
71
+ end
72
+ http.callback do
73
+ if http.response_header.status >= 500
74
+ error = UCEngine::Client::HttpError.new(http.response_header.status, http.response, http.uri)
75
+ response.fail error
76
+ yield error, nil if block_given?
77
+ else
78
+ data = http.response
79
+ if data['error']
80
+ error = UCEngine::Client::UCError.new(http.response_header.status, data['error'], http.req.uri.to_s)
81
+ response.fail error
82
+ yield error, nil if block_given?
83
+ else
84
+ result = data["result"]
85
+ session = UCEngine::Client::Session.new(self, result["uid"], result["sid"])
86
+ response.succeed session
87
+ yield nil, session if block_given?
88
+ end
89
+ end
90
+ end
91
+ response
92
+ end
93
+ end
94
+
95
+ module EventMachineRequest
96
+ def http_request(method, path, params={}, body={}, session=nil, headers=nil, &block)
97
+ opts ||= {:query => params, :body => body, :head => headers}
98
+
99
+ key = (method == :get || method == :delete) ? :query : :body
100
+ opts[key].merge!(:uid => self.uid, :sid => self.sid) if self.class == UCEngine::Client::Session
101
+
102
+ # TODO: make a em-http-request middleware
103
+ if headers && headers['Content-Type'] == 'application/json'
104
+ opts[key] = opts[key].to_json
105
+ end
106
+
107
+ conn = EM::HttpRequest.new(path)
108
+ req = conn.send(method, opts)
109
+ req
110
+ end
111
+
112
+ def get(path, params={}, body={}, session=nil, headers=nil)
113
+ http_request(:get, path, params, body, session, headers)
114
+ end
115
+
116
+ def post(path, params={}, body={}, session=nil, headers={})
117
+ http_request(:post, path, params, body, session, headers)
118
+ end
119
+
120
+ def put(path, params={}, body={}, session=nil, headers={})
121
+ http_request(:put, path, params, body, session, headers)
122
+ end
123
+
124
+ def delete(path, params={}, body={}, session=nil, headers={})
125
+ http_request(:delete, path, params, body, session, headers)
126
+ end
127
+
128
+ # Perform a post request on the API with a content type application/json
129
+ #
130
+ # @param [String] path
131
+ # @param [Hash] body
132
+ def json_post(path, body)
133
+ http_request(:post, path, {}, body, nil, {'Content-Type' => 'application/json'})
134
+ end
135
+ end
136
+
137
+ class Client < ::UCEngine::Client
138
+ include UCEngine::EventMachineResponse
139
+ include UCEngine::EventMachineRequest
140
+
141
+ EM::HttpRequest.use EM::Middleware::JSONResponse
142
+
143
+ def time(&block)
144
+ Session.new(self, nil, nil).time &block
145
+ end
146
+
147
+ # Init EventMachine and init a new instance of U.C.Engine client
148
+ # See #initialize for arguments
149
+ def self.run(*args)
150
+ EM.run { yield self.new(*args) }
151
+ end
152
+
153
+ # Represent a subscription to the U.C.Engine API
154
+ # Use #subscribe to create a new one
155
+ class Subscription < EM::EventSource
156
+ # Start subscription
157
+ def start(start)
158
+ @query[:start] = start
159
+ super()
160
+ end
161
+
162
+ # Cancel subscription
163
+ def cancel
164
+ close
165
+ yield if block_given?
166
+ end
167
+ end
168
+
169
+ class Session < ::UCEngine::Client::Session
170
+ include UCEngine::EventMachineResponse
171
+ include UCEngine::EventMachineRequest
172
+
173
+ # Subscribe to events
174
+ #
175
+ # @param [String] meeting
176
+ # @param [Hash] params
177
+ # @return Subscription
178
+ def subscribe(meeting, params={}, &block)
179
+ params[:mode] = "eventsource"
180
+ params.merge!(:uid => uid, :sid => sid)
181
+ s = Subscription.new(url("/live/#{meeting}"), params)
182
+ time do |err, now|
183
+ s.message do |message|
184
+ block.call(nil, [JSON.parse(message)])
185
+ end
186
+ s.error do |error|
187
+ if s.ready_state != EM::EventSource::CONNECTING
188
+ puts error
189
+ block.call error, nil
190
+ end
191
+ end
192
+ s.start(now)
193
+ end
194
+ s
195
+ end
196
+
197
+ # Upload a file in a meeting room
198
+ #
199
+ # @param [String] meeting name
200
+ # @param [File] file
201
+ # @param [Hash] metadata
202
+ def upload(meeting, file, metadata={}, &block)
203
+ partfile = Part.new( :name => 'content',
204
+ :filename => File.basename(file.path),
205
+ :body => file.read)
206
+ partuid = Part.new( :name => 'uid',
207
+ :body => uid)
208
+ partsid = Part.new( :name => 'sid',
209
+ :body => sid)
210
+ parts = [partfile, partsid, partuid]
211
+ parts << metadata.inject([]) { |array, (key, value)|
212
+ array << Part.new( :name => "metadata[#{key}]",
213
+ :body => value )
214
+ }
215
+
216
+ body = MultipartBody.new(parts)
217
+
218
+ conn = EM::HttpRequest.new(uce.url "/file/#{meeting}")
219
+ req = conn.post( :head => {'content-type' => "multipart/form-data; boundary=#{body.boundary}"},
220
+ :body => "#{body.to_s}\r\n")
221
+ answer(req, &block)
222
+ end
223
+
224
+ # Download a file
225
+ # The result will a File object
226
+ # uce.download("demo", "myfile") do |err, file|
227
+ # puts file.open.read
228
+ # end
229
+ #
230
+ # @param [String] meeting
231
+ # @param [String] filename
232
+ def download(meeting, filename, &block)
233
+ answer_download get(url("/file/#{meeting}/#{filename}")), &block
234
+ end
235
+
236
+ end
237
+ end
238
+ end
239
+ end