bluevia 1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,401 @@
1
+ #
2
+ # BlueVia is a global iniciative of Telefonica delivered by Movistar and O2.
3
+ # Please, check out www.bluevia.com and if you need more information
4
+ # contact us at mailto:support@bluevia.com
5
+
6
+ require 'net/http'
7
+ require 'net/https'
8
+ require 'uri'
9
+ require 'cgi'
10
+ require 'bluevia/utils'
11
+ require 'bluevia/response'
12
+ require 'bluevia/ext/hash'
13
+ require 'bluevia/utils'
14
+ require 'bluevia/errors'
15
+ require 'bluevia/bluevia_logger'
16
+ require 'oauth/client/helper'
17
+
18
+ module Bluevia
19
+ #
20
+ # Abstract class that wraps access to REST services
21
+ #
22
+
23
+ class BaseClient
24
+ include BlueviaLogger
25
+
26
+ attr_accessor :base_uri
27
+
28
+ #
29
+ # REST INTERFACE CONFIGURATION
30
+ #
31
+
32
+ # Base URI to invoke Bluevia API. Production environment
33
+ BASEURI = "https://api.bluevia.com"
34
+
35
+ # Base Path to invoke Bluevia API. Production environment
36
+ BASEPATH = "/services/REST"
37
+
38
+ # Default parameters required to invoke API
39
+ DEFAULT_PARAMS = {:version => "v1", :alt => "json"}
40
+ # Endpoint for commercial (either testing or commercial apps)
41
+ BASEPATH_COMMERCIAL = ""
42
+ # Endpoint for sandbox (either testing or commercial apps)
43
+ BASEPATH_SANDBOX = "_Sandbox"
44
+
45
+ # HTTP Proxy is SDK is being used behind a firewall
46
+ PROXY = nil #"localhost:8888" #, "nube.hi.inet:8080"
47
+
48
+ def BaseClient.create_rest_client(uri = nil)
49
+ @@base_uri = uri.nil? ? BASEURI : uri
50
+ @@uri = URI.parse(@@base_uri)
51
+
52
+ # Set proxy if required
53
+ unless PROXY.nil?
54
+ proxy = PROXY.split(":")
55
+ rest = Net::HTTP::Proxy(proxy[0], proxy[1]).new(@@uri.host, @@uri.port)
56
+ else
57
+ rest = Net::HTTP.new(@@uri.host, @@uri.port)
58
+ end
59
+ # Set HTTP connection with SSL if required
60
+ if @@uri.instance_of?(URI::HTTPS)
61
+ rest.use_ssl = true
62
+ end
63
+ rest.read_timeout=(5)
64
+ rest
65
+ end
66
+
67
+
68
+ #
69
+ # Creates basic HTTP client
70
+ #
71
+ def initialize(params = nil)
72
+ @@uri = URI.parse(BASEURI)
73
+
74
+ if params.nil?
75
+ @rest = BaseClient.create_rest_client
76
+ elsif params.instance_of?(Hash)
77
+ params.has_key?(:rest) and @rest = params[:rest]
78
+ params.has_key?(:logger) and logger = params[:logger]
79
+ else
80
+ @rest = params
81
+ end
82
+
83
+ logger.debug "Initialized baseClient for service: #{self.class.name}"
84
+ end
85
+
86
+ #
87
+ # set HTTP client
88
+ #
89
+ def set_http_client(rest)
90
+ @rest = rest
91
+ end
92
+
93
+ #
94
+ # set the valid timeout for the HTTP requests
95
+ #
96
+ def set_timeout(timeout = 5)
97
+ !timeout.nil? and @rest.read_timeout(timeout)
98
+ end
99
+
100
+ #
101
+ # Define an instance variable using the default Hash syntax
102
+ #
103
+ def []=(key, value)
104
+ self.instance_variable_set(:"@#{key}", value) unless key.nil?
105
+ end
106
+
107
+ #
108
+ # Creates a valid path by concat the path received
109
+ # with the path defined in BASEPATH
110
+ #
111
+ def set_path(_path = nil)
112
+ path = BASEPATH.clone
113
+ path << _path unless _path.nil?
114
+ end
115
+
116
+ #
117
+ # Include params in the URI being created to perform a REST call
118
+ #
119
+ def include_params(_path, _params)
120
+ "#{_path}?".
121
+ concat(
122
+ _params.collect{
123
+ |k,v|
124
+ "#{k}=#{CGI::escape(v.to_s)}"
125
+ }.reverse.join('&')) unless _params.nil?
126
+ end
127
+
128
+ # Each Bluevia API has a specific basepath. This method gets the specific
129
+ # service basepath and the path associated to the request (either test or
130
+ # commercial)
131
+ def get_basepath
132
+ begin
133
+ basepath = self.class.const_get("BASEPATH_API")
134
+ rescue NameError => e
135
+ logger.error "Unable to fetch basepath #{e}"
136
+ basepath = "/#{self.class.to_s.upcase}_"
137
+ end
138
+
139
+ if @commercial
140
+ path = BASEPATH_COMMERCIAL
141
+ else
142
+ path = BASEPATH_SANDBOX
143
+ end
144
+ "#{basepath}#{path}"
145
+ end
146
+
147
+ #
148
+ # Send an Http::Get to the server
149
+ #
150
+ def GET(_path = nil, params = nil, headers = nil, field = nil)
151
+ path = set_path(_path)
152
+ params = Hash.new if params.nil?
153
+ params.merge!(DEFAULT_PARAMS) { |key,oldval,newval| oldval }
154
+
155
+ path = include_params(path, params)
156
+
157
+ logger.debug "GET Request path: " << path
158
+
159
+ begin
160
+ resp = authorized_client.get(path, get_headers(headers))
161
+ rescue => e
162
+ logger.error e
163
+ end
164
+ return handle_response(create_response(resp), "body", field)
165
+ end
166
+
167
+ #
168
+ # Send an Http::Post to the server
169
+ #
170
+ def POST(_path = nil, body = nil, headers = nil, files = nil, return_header = nil)
171
+
172
+ if files.nil?
173
+ path = set_path(_path)
174
+ path = include_params(path, DEFAULT_PARAMS)
175
+ logger.debug "POST Request path: " << path
176
+ logger.debug "POST body: " << body
177
+
178
+ begin
179
+ resp = authorized_client.post(path, body, get_headers(headers, true))
180
+ rescue => e
181
+ logger.error e
182
+ end
183
+ return handle_response(create_response(resp), return_header.nil? ? nil : "headers", return_header)
184
+ else
185
+ return POST_MULTIPART(_path, body, headers, files)
186
+ end
187
+ end
188
+
189
+ #
190
+ # Send an Http::Delete to the server
191
+ #
192
+ def DELETE(_path = nil, headers = nil)
193
+ path = set_path(_path)
194
+ path = include_params(path, DEFAULT_PARAMS)
195
+
196
+ logger.debug "DELETE Request path: " << path
197
+
198
+ resp = authorized_client.delete(path, get_headers(headers, false))
199
+ return handle_response(create_response(resp))
200
+ end
201
+
202
+ def authorized_client
203
+ @consumer ||= OAuth::Consumer.new(@consumer_key, @consumer_secret, :site => @@base_uri)
204
+ @access_token ||= OAuth::AccessToken.new(@consumer, @token, @token_secret)
205
+ @access_token
206
+ end
207
+
208
+ #
209
+ # Creates the basic header while creating an HTTP Request
210
+ #
211
+ def get_headers(_headers = nil, is_post = false)
212
+
213
+ headers = {
214
+ "Accept" => "application/json"
215
+ }
216
+
217
+ if is_post
218
+ headers["Content-Type"] = "application/json"
219
+ end
220
+
221
+ unless _headers.nil?
222
+ headers.merge!(_headers) { |key,oldval,newval| newval }
223
+ end
224
+ logger.debug "HEADERS: "
225
+ logger.debug headers
226
+ return headers
227
+ end
228
+
229
+ private
230
+
231
+ # Converts an HTTP response to an internal object
232
+ def create_response(http_response)
233
+ return nil if http_response.nil?
234
+
235
+ response = Response.new
236
+ response.code = http_response.code
237
+ response.message = http_response.message
238
+
239
+ # headers
240
+ response.headers = Hash.new
241
+
242
+ http_response.each_header {|key, value|
243
+ response.headers[key] = value
244
+ }
245
+
246
+ # body
247
+ begin
248
+ # Convert JSON
249
+ if response.headers["content-type"].to_s.start_with?("application/json")
250
+ unless http_response.body.nil? or http_response.body.empty?
251
+ response.body = JSON.parse(http_response.body)
252
+ else
253
+ response.body = ""
254
+ end
255
+ # Convert XML to Hash
256
+ elsif response.headers["content-type"].to_s.start_with?("application/xml")
257
+ begin
258
+ response.body = Hash.from_xml(http_response.body)
259
+ rescue => e
260
+ logger.error "Unable to decode response: #{e.to_s}"
261
+ raise ServerError, "Unable to decode XML response"
262
+ end
263
+ # Do nothing
264
+ else
265
+ response.body = http_response.body
266
+ end
267
+ rescue => e
268
+ logger.error "Error while converting response"
269
+ logger.error e
270
+ raise ServerError, "Error while processing the response"
271
+ end
272
+ logger.debug response.to_s
273
+ return response
274
+ end
275
+
276
+ #
277
+ # This method is in charge of verify the code retrieved from the server and
278
+ # handle it properly.
279
+ # In case of get a field, just this JSON filed is retrieved from the body response
280
+ #
281
+ # Examples:
282
+ #
283
+ # response.code = 200
284
+ # response.body = {"name": "foo", "surname": "bar", "age": 32, "address": "Nissim Aloni"}
285
+ #
286
+ # handle_response(response, {"name"}) => "foo"
287
+ # handle_response(response) => {"name": "foo", "surname": "bar", "age": 32, "address": "Nissim Aloni"}
288
+ # handle_response(response, {"age"}) => 32
289
+ #
290
+ def handle_response(response, field_name = nil, field_value = nil)
291
+ unless response.nil?
292
+
293
+ if !field_name.nil? && field_value.instance_of?(String)
294
+ field_value = [field_value]
295
+ end
296
+
297
+ # Valid response
298
+ if response.code =~ /20?/
299
+ unless field_name.nil?
300
+ response_field = response[field_name]
301
+ if field_value.nil?
302
+ return response_field
303
+ end
304
+ unless response_field.nil?
305
+ if response_field.instance_of?(Hash)
306
+ unless field_value.nil? || !field_value.instance_of?(Array)
307
+ field_value.each{ |key|
308
+ unless response_field.has_key?(key)
309
+ raise ClientError, "Unable to find the request data: #{key}"
310
+ end
311
+ response_field = response_field[key]
312
+ }
313
+ end
314
+ return response_field
315
+ end
316
+ else
317
+ return false
318
+ end
319
+ else
320
+ return response
321
+ end
322
+ # Not found
323
+ elsif response.code.eql?("404")
324
+ raise NotFoundError, "Unable to find the resource: #{response.message} - #{response.body}"
325
+ # 40?
326
+ elsif response.code =~ /40?/
327
+ raise ClientError, "Error #{response.code} received: #{response.message} - #{response.body}"
328
+ # Not implemented
329
+ elsif response.code =~ /501/
330
+ raise NotImplementedError, "Operation not implemented in server side #{response.message} - #{response.body}"
331
+ # Server Error
332
+ elsif response.code =~ /500/
333
+ raise ServerError, "Server error #{response.message} - #{response.body}"
334
+ end
335
+ else
336
+ raise ServerError, "Unable to connect with endpoint."
337
+ end
338
+ end
339
+
340
+ #
341
+ # Send an Http::Post::Multipart to the server
342
+ #
343
+ def POST_MULTIPART(_path = nil, body = nil, headers = nil, files = nil)
344
+ path = set_path(_path)
345
+ path = include_params(path, DEFAULT_PARAMS)
346
+ unless files.nil?
347
+ logger.debug "POST MULTIPART " << path
348
+ multipart_data = Hash.new
349
+ #prueba = Utils::Multipart.new
350
+ unless files.nil?
351
+ if files.instance_of?(String) or (files.instance_of?(Array) and files.length == 1)
352
+ if files.instance_of?(Array)
353
+ files = files[0]
354
+ end
355
+ multipart_data["attachments"] = UploadIO.new(files, Utils.get_file_mime_type(files))
356
+ elsif files.instance_of?(Array)
357
+ requestbody = String.new
358
+ mimeboundary = "ABC123Boundary"
359
+ files.each{|file|
360
+ requestbody << "\r\n"
361
+ requestbody << "--#{mimeboundary}\n"
362
+ requestbody << "\r\n"
363
+ requestbody << "Content-Disposition: attachment; fileName=\"text.txt\""
364
+ requestbody << "\r\n"
365
+ requestbody << "Content-Type: #{Utils.get_file_mime_type(file)}\n"
366
+ requestbody << "\r\n"
367
+ content = File.open(file, "rb")
368
+ requestbody << "#{content.read}\n"
369
+ }
370
+ requestbody << "--#{mimeboundary}--\n"
371
+ multipart_data["attachment"] = UploadIO.new(StringIO.new(requestbody), "multipart/mixed; boundary=#{mimeboundary}", "attachments")
372
+ elsif files.instance_of?(Hash) && files.has_key?(:text)
373
+ multipart_data["attachments"] = UploadIO.new(StringIO.new(files[:text]), "text/plain", "text")
374
+ else
375
+ logger.error "Not sure about how to send this object: #{files.class.name}"
376
+ end
377
+ end
378
+ multipart_data["message"] = UploadIO.new(StringIO.new(body), "application/json", "root-field")
379
+ logger.debug "MULTIPART POST message: #{body}"
380
+ req = Net::HTTP::Post::Multipart.new(
381
+ path,
382
+ multipart_data,
383
+ get_headers(headers, true)
384
+ )
385
+
386
+ # Set proxy if required
387
+ unless PROXY.nil?
388
+ proxy = PROXY.split(":")
389
+ rest = Net::HTTP::Proxy(proxy[0], proxy[1]).new(@@uri.host,@@uri.port)
390
+ else
391
+ rest = Net::HTTP.new(@@uri.host,@@uri.port)
392
+ end
393
+ res = rest.start do |http|
394
+ http.request(req)
395
+ end
396
+
397
+ return create_response(res)
398
+ end
399
+ end
400
+ end
401
+ end
@@ -0,0 +1,119 @@
1
+ #
2
+ # BlueVia is a global iniciative of Telefonica delivered by Movistar and O2.
3
+ # Please, check out www.bluevia.com and if you need more information
4
+ # contact us at mailto:support@bluevia.com
5
+
6
+ require 'rubygems'
7
+ require 'json'
8
+ require 'uri'
9
+ require 'net/http'
10
+ require 'bluevia/directory'
11
+ require 'bluevia/sms'
12
+ require 'bluevia/oauth'
13
+ require 'bluevia/advertising'
14
+ require 'bluevia/bluevia_logger'
15
+
16
+ module Bluevia
17
+ #
18
+ # This class is the main client to access Bluevia API.
19
+ # It defines a services factory that provides an isolated way to reach
20
+ # each API. Currently the following APIs are supported:
21
+ # * oAuth authentication
22
+ # * SMS
23
+ # * Advertising
24
+ # * Directory
25
+ #
26
+ # When creating a BlueviaClient instance, basic authentication parameters
27
+ # can be provided:
28
+ #
29
+ # require 'bluevia'
30
+ #
31
+ # bc = BlueviaClient.new(
32
+ # { :consumer_key => <YOUR_CONSUMER_KEY>,
33
+ # :consumer_secret => <YOUR_CONSUMER_SECRET>,
34
+ # :token => <OAUTH_TOKEN>,
35
+ # :token_secret => <OAUTH_TOKEN_SECRET>
36
+ # })
37
+ #
38
+
39
+ class BlueviaClient
40
+ include BlueviaLogger
41
+
42
+ # Constructor
43
+ def initialize(params = nil)
44
+
45
+ unless params.nil? || params[:uri].nil?
46
+ rest = BaseClient.create_rest_client(params[:uri])
47
+ else
48
+ rest = BaseClient.create_rest_client
49
+ end
50
+
51
+ @commercial = false
52
+ @consumer_key = nil
53
+ @consumer_secret = nil
54
+
55
+ unless params.nil?
56
+ %w(consumer_key consumer_secret token token_secret).each{ |param|
57
+ self.instance_variable_set(:"@#{param}", params[:"#{param}"]) unless params[:"#{param}"].nil?
58
+ }
59
+ end
60
+
61
+ @factory = ServicesFactory.new(rest)
62
+ end
63
+
64
+ # Retrieves a specific service to call any related API
65
+ # i.e.
66
+ # sms = bc.get_service(:Sms)
67
+ def get_service(_service)
68
+ service = @factory.get(_service)
69
+ service[:consumer_key] = @consumer_key unless @consumer_key.nil?
70
+ service[:consumer_secret] = @consumer_secret unless @consumer_secret.nil?
71
+ service[:token] = @token unless @token.nil?
72
+ service[:token_secret] = @token_secret unless @token_secret.nil?
73
+ service[:commercial] = @commercial
74
+ return service
75
+ end
76
+
77
+ # Client will get access to commercial APIs
78
+ def set_commercial
79
+ @commercial = true
80
+ end
81
+
82
+ # Client will get access to sandbox APIs
83
+ def set_sandbox
84
+ @commercial = false
85
+ end
86
+
87
+ # {true|false} if commercial or sandbox client
88
+ def commercial?
89
+ return @commercial
90
+ end
91
+ end
92
+
93
+ # This service factory wraps the initialization of a service
94
+ class ServicesFactory
95
+ include BlueviaLogger
96
+ def initialize(rest = nil)
97
+ @directory = Directory.new({:rest =>rest, :logger => logger})
98
+ @sms = Sms.new({:rest =>rest, :logger => logger})
99
+ @advertising = Advertising.new({:rest =>rest, :logger => logger})
100
+ @oauth = Oauth.new({:rest =>rest, :logger => logger})
101
+ end
102
+
103
+ def get(service)
104
+ case service.to_s.downcase
105
+ when "directory"
106
+ return @directory
107
+ when "sms"
108
+ return @sms
109
+ when "oauth"
110
+ return @oauth
111
+ when "advertising"
112
+ return @advertising
113
+ else
114
+ raise(SyntaxError, "Service not valid")
115
+ end
116
+ end
117
+
118
+ end
119
+ end