em-ucengine 0.1.1 → 0.3.0

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